123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 |
- =====================================
- Writing your first Django app, part 1
- =====================================
- Let's learn by example.
- Throughout this tutorial, we'll walk you through the creation of a basic
- poll application.
- It'll consist of two parts:
- * A public site that lets people view polls and vote in them.
- * An admin site that lets you add, change and delete polls.
- We'll assume you have :doc:`Django installed </intro/install>` already. You can
- tell Django is installed and which version by running the following command:
- .. code-block:: bash
- $ python -c "import django; print(django.get_version())"
- If Django is installed, you should see the version of your installation. If it
- isn't, you'll get an error telling "No module named django".
- This tutorial is written for Django |version| and Python 3.3 or later. If the
- Django version doesn't match, you can refer to the tutorial for your version
- of Django by using the version switcher at the bottom right corner of this
- page, or update Django to the newest version. If you are still using Python
- 2.7, you will need to adjust the code samples slightly, as described in
- comments.
- See :doc:`How to install Django </topics/install>` for advice on how to remove
- older versions of Django and install a newer one.
- .. admonition:: Where to get help:
- If you're having trouble going through this tutorial, please post a message
- to |django-users| or drop by `#django on irc.freenode.net
- <irc://irc.freenode.net/django>`_ to chat with other Django users who might
- be able to help.
- Creating a project
- ==================
- If this is your first time using Django, you'll have to take care of some
- initial setup. Namely, you'll need to auto-generate some code that establishes a
- Django :term:`project` -- a collection of settings for an instance of Django,
- including database configuration, Django-specific options and
- application-specific settings.
- From the command line, ``cd`` into a directory where you'd like to store your
- code, then run the following command:
- .. code-block:: bash
- $ django-admin startproject mysite
- This will create a ``mysite`` directory in your current directory. If it didn't
- work, see :ref:`troubleshooting-django-admin`.
- .. note::
- You'll need to avoid naming projects after built-in Python or Django
- components. In particular, this means you should avoid using names like
- ``django`` (which will conflict with Django itself) or ``test`` (which
- conflicts with a built-in Python package).
- .. admonition:: Where should this code live?
- If your background is in plain old PHP (with no use of modern frameworks),
- you're probably used to putting code under the Web server's document root
- (in a place such as ``/var/www``). With Django, you don't do that. It's
- not a good idea to put any of this Python code within your Web server's
- document root, because it risks the possibility that people may be able
- to view your code over the Web. That's not good for security.
- Put your code in some directory **outside** of the document root, such as
- :file:`/home/mycode`.
- Let's look at what :djadmin:`startproject` created::
- mysite/
- manage.py
- mysite/
- __init__.py
- settings.py
- urls.py
- wsgi.py
- These files are:
- * The outer :file:`mysite/` root directory is just a container for your
- project. Its name doesn't matter to Django; you can rename it to anything
- you like.
- * :file:`manage.py`: A command-line utility that lets you interact with this
- Django project in various ways. You can read all the details about
- :file:`manage.py` in :doc:`/ref/django-admin`.
- * The inner :file:`mysite/` directory is the actual Python package for your
- project. Its name is the Python package name you'll need to use to import
- anything inside it (e.g. ``mysite.urls``).
- * :file:`mysite/__init__.py`: An empty file that tells Python that this
- directory should be considered a Python package. (Read `more about
- packages`_ in the official Python docs if you're a Python beginner.)
- * :file:`mysite/settings.py`: Settings/configuration for this Django
- project. :doc:`/topics/settings` will tell you all about how settings
- work.
- * :file:`mysite/urls.py`: The URL declarations for this Django project; a
- "table of contents" of your Django-powered site. You can read more about
- URLs in :doc:`/topics/http/urls`.
- * :file:`mysite/wsgi.py`: An entry-point for WSGI-compatible web servers to
- serve your project. See :doc:`/howto/deployment/wsgi/index` for more details.
- .. _more about packages: https://docs.python.org/tutorial/modules.html#packages
- Database setup
- --------------
- Now, edit :file:`mysite/settings.py`. It's a normal Python module with
- module-level variables representing Django settings.
- By default, the configuration uses SQLite. If you're new to databases, or
- you're just interested in trying Django, this is the easiest choice. SQLite is
- included in Python, so you won't need to install anything else to support your
- database.
- If you wish to use another database, install the appropriate :ref:`database
- bindings <database-installation>`, and change the following keys in the
- :setting:`DATABASES` ``'default'`` item to match your database connection
- settings:
- * :setting:`ENGINE <DATABASE-ENGINE>` -- Either
- ``'django.db.backends.sqlite3'``,
- ``'django.db.backends.postgresql_psycopg2'``,
- ``'django.db.backends.mysql'``, or
- ``'django.db.backends.oracle'``. Other backends are :ref:`also available
- <third-party-notes>`.
- * :setting:`NAME` -- The name of your database. If you're using SQLite, the
- database will be a file on your computer; in that case, :setting:`NAME`
- should be the full absolute path, including filename, of that file. The
- default value, ``os.path.join(BASE_DIR, 'db.sqlite3')``, will store the file
- in your project directory.
- If you are not using SQLite as your database, additional settings such as :setting:`USER`, :setting:`PASSWORD`, :setting:`HOST` must be added.
- For more details, see the reference documentation for :setting:`DATABASES`.
- .. note::
- If you're using PostgreSQL or MySQL, make sure you've created a database by
- this point. Do that with "``CREATE DATABASE database_name;``" within your
- database's interactive prompt.
- If you're using SQLite, you don't need to create anything beforehand - the
- database file will be created automatically when it is needed.
- While you're editing :file:`mysite/settings.py`, set :setting:`TIME_ZONE` to
- your time zone.
- Also, note the :setting:`INSTALLED_APPS` setting at the top of the file. That
- holds the names of all Django applications that are activated in this Django
- instance. Apps can be used in multiple projects, and you can package and
- distribute them for use by others in their projects.
- By default, :setting:`INSTALLED_APPS` contains the following apps, all of which
- come with Django:
- * :mod:`django.contrib.admin` -- The admin site. You'll use it in :doc:`part 2
- of this tutorial </intro/tutorial02>`.
- * :mod:`django.contrib.auth` -- An authentication system.
- * :mod:`django.contrib.contenttypes` -- A framework for content types.
- * :mod:`django.contrib.sessions` -- A session framework.
- * :mod:`django.contrib.messages` -- A messaging framework.
- * :mod:`django.contrib.staticfiles` -- A framework for managing
- static files.
- These applications are included by default as a convenience for the common case.
- Some of these applications makes use of at least one database table, though,
- so we need to create the tables in the database before we can use them. To do
- that, run the following command:
- .. code-block:: bash
- $ python manage.py migrate
- The :djadmin:`migrate` command looks at the :setting:`INSTALLED_APPS` setting
- and creates any necessary database tables according to the database settings
- in your :file:`mysite/settings.py` file and the database migrations shipped
- with the app (we'll cover those later). You'll see a message for each
- migration it applies. If you're interested, run the command-line client for your
- database and type ``\dt`` (PostgreSQL), ``SHOW TABLES;`` (MySQL), or
- ``.schema`` (SQLite) to display the tables Django created.
- .. admonition:: For the minimalists
- Like we said above, the default applications are included for the common
- case, but not everybody needs them. If you don't need any or all of them,
- feel free to comment-out or delete the appropriate line(s) from
- :setting:`INSTALLED_APPS` before running :djadmin:`migrate`. The
- :djadmin:`migrate` command will only run migrations for apps in
- :setting:`INSTALLED_APPS`.
- The development server
- ----------------------
- Let's verify your Django project works. Change into the outer :file:`mysite` directory, if
- you haven't already, and run the following commands:
- .. code-block:: bash
- $ python manage.py runserver
- You'll see the following output on the command line:
- .. parsed-literal::
- Performing system checks...
- 0 errors found
- |today| - 15:50:53
- Django version |version|, using settings 'mysite.settings'
- Starting development server at http://127.0.0.1:8000/
- Quit the server with CONTROL-C.
- You've started the Django development server, a lightweight Web server written
- purely in Python. We've included this with Django so you can develop things
- rapidly, without having to deal with configuring a production server -- such as
- Apache -- until you're ready for production.
- Now's a good time to note: **don't** use this server in anything resembling a
- production environment. It's intended only for use while developing. (We're in
- the business of making Web frameworks, not Web servers.)
- Now that the server's running, visit http://127.0.0.1:8000/ with your Web
- browser. You'll see a "Welcome to Django" page, in pleasant, light-blue pastel.
- It worked!
- .. admonition:: Changing the port
- By default, the :djadmin:`runserver` command starts the development server
- on the internal IP at port 8000.
- If you want to change the server's port, pass
- it as a command-line argument. For instance, this command starts the server
- on port 8080:
- .. code-block:: bash
- $ python manage.py runserver 8080
- If you want to change the server's IP, pass it along with the port. So to
- listen on all public IPs (useful if you want to show off your work on other
- computers), use:
- .. code-block:: bash
- $ python manage.py runserver 0.0.0.0:8000
- Full docs for the development server can be found in the
- :djadmin:`runserver` reference.
- .. admonition:: Automatic reloading of :djadmin:`runserver`
- The development server automatically reloads Python code for each request
- as needed. You don't need to restart the server for code changes to take
- effect. However, some actions like adding files don't trigger a restart,
- so you'll have to restart the server in these cases.
- .. _creating-models:
- Creating models
- ===============
- Now that your environment -- a "project" -- is set up, you're set to start
- doing work.
- Each application you write in Django consists of a Python package that follows
- a certain convention. Django comes with a utility that automatically generates
- the basic directory structure of an app, so you can focus on writing code
- rather than creating directories.
- .. admonition:: Projects vs. apps
- What's the difference between a project and an app? An app is a Web
- application that does something -- e.g., a Weblog system, a database of
- public records or a simple poll app. A project is a collection of
- configuration and apps for a particular Web site. A project can contain
- multiple apps. An app can be in multiple projects.
- Your apps can live anywhere on your `Python path`_. In this tutorial, we'll
- create our poll app right next to your :file:`manage.py` file so that it can be
- imported as its own top-level module, rather than a submodule of ``mysite``.
- To create your app, make sure you're in the same directory as :file:`manage.py`
- and type this command:
- .. code-block:: bash
- $ python manage.py startapp polls
- That'll create a directory :file:`polls`, which is laid out like this::
- polls/
- __init__.py
- admin.py
- migrations/
- __init__.py
- models.py
- tests.py
- views.py
- This directory structure will house the poll application.
- The first step in writing a database Web app in Django is to define your models
- -- essentially, your database layout, with additional metadata.
- .. admonition:: Philosophy
- A model is the single, definitive source of truth about your data. It contains
- the essential fields and behaviors of the data you're storing. Django follows
- the :ref:`DRY Principle <dry>`. The goal is to define your data model in one
- place and automatically derive things from it.
- This includes the migrations - unlike in Ruby On Rails, for example, migrations
- are entirely derived from your models file, and are essentially just a
- history that Django can roll through to update your database schema to
- match your current models.
- In our simple poll app, we'll create two models: ``Question`` and ``Choice``.
- A ``Question`` has a question and a publication date. A ``Choice`` has two fields:
- the text of the choice and a vote tally. Each ``Choice`` is associated with a
- ``Question``.
- These concepts are represented by simple Python classes. Edit the
- :file:`polls/models.py` file so it looks like this:
- .. snippet::
- :filename: polls/models.py
- from django.db import models
- class Question(models.Model):
- question_text = models.CharField(max_length=200)
- pub_date = models.DateTimeField('date published')
- class Choice(models.Model):
- question = models.ForeignKey(Question)
- choice_text = models.CharField(max_length=200)
- votes = models.IntegerField(default=0)
- The code is straightforward. Each model is represented by a class that
- subclasses :class:`django.db.models.Model`. Each model has a number of class
- variables, each of which represents a database field in the model.
- Each field is represented by an instance of a :class:`~django.db.models.Field`
- class -- e.g., :class:`~django.db.models.CharField` for character fields and
- :class:`~django.db.models.DateTimeField` for datetimes. This tells Django what
- type of data each field holds.
- The name of each :class:`~django.db.models.Field` instance (e.g. ``question_text`` or
- ``pub_date``) is the field's name, in machine-friendly format. You'll use this
- value in your Python code, and your database will use it as the column name.
- You can use an optional first positional argument to a
- :class:`~django.db.models.Field` to designate a human-readable name. That's used
- in a couple of introspective parts of Django, and it doubles as documentation.
- If this field isn't provided, Django will use the machine-readable name. In this
- example, we've only defined a human-readable name for ``Question.pub_date``. For all
- other fields in this model, the field's machine-readable name will suffice as
- its human-readable name.
- Some :class:`~django.db.models.Field` classes have required arguments.
- :class:`~django.db.models.CharField`, for example, requires that you give it a
- :attr:`~django.db.models.CharField.max_length`. That's used not only in the
- database schema, but in validation, as we'll soon see.
- A :class:`~django.db.models.Field` can also have various optional arguments; in
- this case, we've set the :attr:`~django.db.models.Field.default` value of
- ``votes`` to 0.
- Finally, note a relationship is defined, using
- :class:`~django.db.models.ForeignKey`. That tells Django each ``Choice`` is related
- to a single ``Question``. Django supports all the common database relationships:
- many-to-one, many-to-many and one-to-one.
- .. _`Python path`: https://docs.python.org/tutorial/modules.html#the-module-search-path
- Activating models
- =================
- That small bit of model code gives Django a lot of information. With it, Django
- is able to:
- * Create a database schema (``CREATE TABLE`` statements) for this app.
- * Create a Python database-access API for accessing ``Question`` and ``Choice`` objects.
- But first we need to tell our project that the ``polls`` app is installed.
- .. admonition:: Philosophy
- Django apps are "pluggable": You can use an app in multiple projects, and
- you can distribute apps, because they don't have to be tied to a given
- Django installation.
- Edit the :file:`mysite/settings.py` file again, and change the
- :setting:`INSTALLED_APPS` setting to include the string ``'polls'``. So it'll
- look like this:
- .. snippet::
- :filename: mysite/settings.py
- INSTALLED_APPS = (
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'polls',
- )
- Now Django knows to include the ``polls`` app. Let's run another command:
- .. code-block:: bash
- $ python manage.py makemigrations polls
- You should see something similar to the following:
- .. code-block:: text
- Migrations for 'polls':
- 0001_initial.py:
- - Create model Question
- - Create model Choice
- - Add field question to choice
- By running ``makemigrations``, you're telling Django that you've made
- some changes to your models (in this case, you've made new ones) and that
- you'd like the changes to be stored as a *migration*.
- Migrations are how Django stores changes to your models (and thus your
- database schema) - they're just files on disk. You can read the migration
- for your new model if you like; it's the file
- ``polls/migrations/0001_initial.py``. Don't worry, you're not expected to read
- them every time Django makes one, but they're designed to be human-editable
- in case you want to manually tweak how Django changes things.
- There's a command that will run the migrations for you and manage your database
- schema automatically - that's called :djadmin:`migrate`, and we'll come to it in a
- moment - but first, let's see what SQL that migration would run. The
- :djadmin:`sqlmigrate` command takes migration names and returns their SQL:
- .. code-block:: bash
- $ python manage.py sqlmigrate polls 0001
- You should see something similar to the following (we've reformatted it for
- readability):
- .. code-block:: sql
- BEGIN;
- CREATE TABLE polls_question (
- "id" serial NOT NULL PRIMARY KEY,
- "question_text" varchar(200) NOT NULL,
- "pub_date" timestamp with time zone NOT NULL
- );
- CREATE TABLE polls_choice (
- "id" serial NOT NULL PRIMARY KEY,
- "question_id" integer NOT NULL,
- "choice_text" varchar(200) NOT NULL,
- "votes" integer NOT NULL
- );
- CREATE INDEX polls_choice_7aa0f6ee ON "polls_choice" ("question_id");
- ALTER TABLE "polls_choice"
- ADD CONSTRAINT polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id
- FOREIGN KEY ("question_id")
- REFERENCES "polls_question" ("id")
- DEFERRABLE INITIALLY DEFERRED;
- COMMIT;
- Note the following:
- * The exact output will vary depending on the database you are using. The
- example above is generated for PostgreSQL.
- * Table names are automatically generated by combining the name of the app
- (``polls``) and the lowercase name of the model -- ``question`` and
- ``choice``. (You can override this behavior.)
- * Primary keys (IDs) are added automatically. (You can override this, too.)
- * By convention, Django appends ``"_id"`` to the foreign key field name.
- (Yes, you can override this, as well.)
- * The foreign key relationship is made explicit by a ``FOREIGN KEY``
- constraint. Don't worry about the ``DEFERRABLE`` parts; that's just telling
- PostgreSQL to not enforce the foreign key until the end of the transaction.
- * It's tailored to the database you're using, so database-specific field types
- such as ``auto_increment`` (MySQL), ``serial`` (PostgreSQL), or ``integer
- primary key autoincrement`` (SQLite) are handled for you automatically. Same
- goes for quoting of field names -- e.g., using double quotes or single
- quotes.
- * The :djadmin:`sqlmigrate` command doesn't actually run the migration on your
- database - it just prints it to the screen so that you can see what SQL
- Django thinks is required. It's useful for checking what Django is going to
- do or if you have database administrators who require SQL scripts for
- changes.
- If you're interested, you can also run
- :djadmin:`python manage.py check <check>`; this checks for any problems in
- your project without making migrations or touching the database.
- Now, run :djadmin:`migrate` again to create those model tables in your database:
- .. code-block:: bash
- $ python manage.py migrate
- Operations to perform:
- Apply all migrations: admin, contenttypes, polls, auth, sessions
- Running migrations:
- Rendering model states... DONE
- ...
- Applying polls.0001_initial... OK
- ...
- The :djadmin:`migrate` command takes all the migrations that haven't been
- applied (Django tracks which ones are applied using a special table in your
- database called ``django_migrations``) and runs them against your database -
- essentially, synchronizing the changes you made to your models with the schema
- in the database.
- Migrations are very powerful and let you change your models over time, as you
- develop your project, without the need to delete your database or tables and
- make new ones - it specializes in upgrading your database live, without
- losing data. We'll cover them in more depth in a later part of the tutorial,
- but for now, remember the three-step guide to making model changes:
- * Change your models (in ``models.py``).
- * Run :djadmin:`python manage.py makemigrations <makemigrations>` to create
- migrations for those changes
- * Run :djadmin:`python manage.py migrate <migrate>` to apply those changes to
- the database.
- The reason there's separate commands to make and apply migrations is because
- you'll commit migrations to your version control system and ship them with
- your app; they not only make your development easier, they're also useable by
- other developers and in production.
- Read the :doc:`django-admin documentation </ref/django-admin>` for full
- information on what the ``manage.py`` utility can do.
- Playing with the API
- ====================
- Now, let's hop into the interactive Python shell and play around with the free
- API Django gives you. To invoke the Python shell, use this command:
- .. code-block:: bash
- $ python manage.py shell
- We're using this instead of simply typing "python", because :file:`manage.py`
- sets the ``DJANGO_SETTINGS_MODULE`` environment variable, which gives Django
- the Python import path to your :file:`mysite/settings.py` file.
- .. admonition:: Bypassing manage.py
- If you'd rather not use :file:`manage.py`, no problem. Just set the
- :envvar:`DJANGO_SETTINGS_MODULE` environment variable to
- ``mysite.settings``, start a plain Python shell, and set up Django:
- .. code-block:: pycon
- >>> import django
- >>> django.setup()
- If this raises an :exc:`AttributeError`, you're probably using
- a version of Django that doesn't match this tutorial version. You'll want
- to either switch to the older tutorial or the newer Django version.
- You must run ``python`` from the same directory :file:`manage.py` is in,
- or ensure that directory is on the Python path, so that ``import mysite``
- works.
- For more information on all of this, see the :doc:`django-admin
- documentation </ref/django-admin>`.
- Once you're in the shell, explore the :doc:`database API </topics/db/queries>`::
- >>> from polls.models import Question, Choice # Import the model classes we just wrote.
- # No questions are in the system yet.
- >>> Question.objects.all()
- []
- # Create a new Question.
- # Support for time zones is enabled in the default settings file, so
- # Django expects a datetime with tzinfo for pub_date. Use timezone.now()
- # instead of datetime.datetime.now() and it will do the right thing.
- >>> from django.utils import timezone
- >>> q = Question(question_text="What's new?", pub_date=timezone.now())
- # Save the object into the database. You have to call save() explicitly.
- >>> q.save()
- # Now it has an ID. Note that this might say "1L" instead of "1", depending
- # on which database you're using. That's no biggie; it just means your
- # database backend prefers to return integers as Python long integer
- # objects.
- >>> q.id
- 1
- # Access model field values via Python attributes.
- >>> q.question_text
- "What's new?"
- >>> q.pub_date
- datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)
- # Change values by changing the attributes, then calling save().
- >>> q.question_text = "What's up?"
- >>> q.save()
- # objects.all() displays all the questions in the database.
- >>> Question.objects.all()
- [<Question: Question object>]
- Wait a minute. ``<Question: Question object>`` is, utterly, an unhelpful representation
- of this object. Let's fix that by editing the ``Question`` model (in the
- ``polls/models.py`` file) and adding a
- :meth:`~django.db.models.Model.__str__` method to both ``Question`` and
- ``Choice``:
- .. snippet::
- :filename: polls/models.py
- from django.db import models
- class Question(models.Model):
- # ...
- def __str__(self): # __unicode__ on Python 2
- return self.question_text
- class Choice(models.Model):
- # ...
- def __str__(self): # __unicode__ on Python 2
- return self.choice_text
- It's important to add :meth:`~django.db.models.Model.__str__` methods to your
- models, not only for your own sanity when dealing with the interactive prompt,
- but also because objects' representations are used throughout Django's
- automatically-generated admin.
- .. admonition:: ``__str__`` or ``__unicode__``?
- On Python 3, it's easy, just use
- :meth:`~django.db.models.Model.__str__`.
- On Python 2, you should define :meth:`~django.db.models.Model.__unicode__`
- methods returning ``unicode`` values instead. Django models have a default
- :meth:`~django.db.models.Model.__str__` method that calls
- :meth:`~django.db.models.Model.__unicode__` and converts the result to a
- UTF-8 bytestring. This means that ``unicode(p)`` will return a Unicode
- string, and ``str(p)`` will return a bytestring, with characters encoded
- as UTF-8. Python does the opposite: ``object`` has a ``__unicode__``
- method that calls ``__str__`` and interprets the result as an ASCII
- bytestring. This difference can create confusion.
- If all of this is gibberish to you, just use Python 3.
- Note these are normal Python methods. Let's add a custom method, just for
- demonstration:
- .. snippet::
- :filename: polls/models.py
- import datetime
- from django.db import models
- from django.utils import timezone
- class Question(models.Model):
- # ...
- def was_published_recently(self):
- return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
- Note the addition of ``import datetime`` and ``from django.utils import
- timezone``, to reference Python's standard :mod:`datetime` module and Django's
- time-zone-related utilities in :mod:`django.utils.timezone`, respectively. If
- you aren't familiar with time zone handling in Python, you can learn more in
- the :doc:`time zone support docs </topics/i18n/timezones>`.
- Save these changes and start a new Python interactive shell by running
- ``python manage.py shell`` again::
- >>> from polls.models import Question, Choice
- # Make sure our __str__() addition worked.
- >>> Question.objects.all()
- [<Question: What's up?>]
- # Django provides a rich database lookup API that's entirely driven by
- # keyword arguments.
- >>> Question.objects.filter(id=1)
- [<Question: What's up?>]
- >>> Question.objects.filter(question_text__startswith='What')
- [<Question: What's up?>]
- # Get the question that was published this year.
- >>> from django.utils import timezone
- >>> current_year = timezone.now().year
- >>> Question.objects.get(pub_date__year=current_year)
- <Question: What's up?>
- # Request an ID that doesn't exist, this will raise an exception.
- >>> Question.objects.get(id=2)
- Traceback (most recent call last):
- ...
- DoesNotExist: Question matching query does not exist.
- # Lookup by a primary key is the most common case, so Django provides a
- # shortcut for primary-key exact lookups.
- # The following is identical to Question.objects.get(id=1).
- >>> Question.objects.get(pk=1)
- <Question: What's up?>
- # Make sure our custom method worked.
- >>> q = Question.objects.get(pk=1)
- >>> q.was_published_recently()
- True
- # Give the Question a couple of Choices. The create call constructs a new
- # Choice object, does the INSERT statement, adds the choice to the set
- # of available choices and returns the new Choice object. Django creates
- # a set to hold the "other side" of a ForeignKey relation
- # (e.g. a question's choice) which can be accessed via the API.
- >>> q = Question.objects.get(pk=1)
- # Display any choices from the related object set -- none so far.
- >>> q.choice_set.all()
- []
- # Create three choices.
- >>> q.choice_set.create(choice_text='Not much', votes=0)
- <Choice: Not much>
- >>> q.choice_set.create(choice_text='The sky', votes=0)
- <Choice: The sky>
- >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
- # Choice objects have API access to their related Question objects.
- >>> c.question
- <Question: What's up?>
- # And vice versa: Question objects get access to Choice objects.
- >>> q.choice_set.all()
- [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
- >>> q.choice_set.count()
- 3
- # The API automatically follows relationships as far as you need.
- # Use double underscores to separate relationships.
- # This works as many levels deep as you want; there's no limit.
- # Find all Choices for any question whose pub_date is in this year
- # (reusing the 'current_year' variable we created above).
- >>> Choice.objects.filter(question__pub_date__year=current_year)
- [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
- # Let's delete one of the choices. Use delete() for that.
- >>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
- >>> c.delete()
- For more information on model relations, see :doc:`Accessing related objects
- </ref/models/relations>`. For more on how to use double underscores to perform
- field lookups via the API, see :ref:`Field lookups <field-lookups-intro>`. For
- full details on the database API, see our :doc:`Database API reference
- </topics/db/queries>`.
- When you're comfortable with the API, read :doc:`part 2 of this tutorial
- </intro/tutorial02>` to get Django's automatic admin working.
|