This is a material for study sessions. In this tutorial, a supplementary explanation for Django's Model and I will introduce a library that extends the functions of django, and try various operations on the shell.
There was an opinion that the relationship between Model, Field, and instance is difficult to understand, so I will supplement it.
Model represents a DB table.
It can be changed by setting the table name to Meta, but by default it will be named app name_model name
.
For the Question model in the polls app, the table name will be polls_question
.
The models. ~ Field defined in the Model correspond to the columns on the table. The id column is automatically added if there is no field for which PrimaryKey is set as.
An instance of Model means one record on the DB table.
polls/models.py
class Question(models.Model):
class Meta:
verbose_name = 'Question'
verbose_name_plural = 'Plural of questions'
ordering = ['-pub_date']
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
It is necessary to issue a SQL statement to get the record from the table on the DB. To do this, Model has a Manager class. The QuerySet class is responsible for the actual issuance and generation of SQL, and after issuing sql, the instance of the base Model is returned. Operations such as record acquisition and creation are performed through this QuerySet class. The Manager class acts as a relay between Model and QuerySet, and is especially closely related to QuerySet.
QuerySet also has methods like get
and create
to get and create records pinpointly.
Since iterator is provided, the records (= instances) to be acquired can be acquired in order by turning itself in a for loop.
qs = Question.objects.all()
for q in qs:
# q <---This is the acquired record and becomes an instance of Question
Source → 8a9d88559ae94bea8bb706468eaa6459127c6f59
Since Model = table and instance = record, the process you want to perform on the record is defined by the instance method. Conversely, the processing you want to perform on the table itself is defined as a class method.
For example, the was_published_recently
created in the tutorial
It is an instance method because it is "determination of whether the record (= instance) has been published recently".
Apart from this, in the case of the method "Get published from Question table", make it a class method.
polls/models/py
class Question(models.Model):
...
@classmethod
def get_published_data(cls):
return cls.objects.filter(pub_date__lte=timezone.now())
In the case of a filter that gets "published" as shown in the example It's a bit verbose, but it's also a good idea to extend the QuerySet.
polls/models.py(QuerySet extension)
import datetime
from django.db import models
from django.utils import timezone
class QuestionQuerySet(models.query.QuerySet):
def is_published(self):
return self.filter(pub_date__lte=timezone.now())
class Question(models.Model):
...
objects = models.Manager.from_queryset(QuestionQuerySet)()
...
@classmethod
def get_published_data(cls):
return cls.objects.is_published()
In either case
You can get the published queryset by doing Question.get_published_data ()
.
However, when Model is extended directly, pk is 10 or less and has been published
.
You cannot put a condition in the middle.
Question.get_published_date().filter(pk__lte=10)
Like, if the condition is "published, pk is 10 or less", it will be applied.
On the other hand, in the case of the extension of QuerySet, you can apply a filter to get "published ones" wherever you like.
Question.objects.filter(pk__lte=10).is_published()
In django, you can directly operate Model etc. by operating with manage.py shell
command.
I will introduce a library to use the shell a little more conveniently.
First of all, ʻipython. If you put this in, it will be able to colorize the shell and complete commands. Next,
django-extensions`.
It provides various extensions to django, as the name implies, as well as the shell.
Both can be easily installed with pip, so please try them.
$ pip install ipython
$ pip install django-extensions
For ipython, just execute $ ./manage.py shell
and the appearance will change automatically.
django-extensions needs to be added to INSTALL_APPS in settings.
tutorial/settings.py
...
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_extensions', #←←← Add this
'bootstrap3',
'polls',
)
...
If set to ʻINSTALLED_APPS, the
manage.py` command will allow you to run commands for django_extensions.
$ ./manage.py
Type 'manage.py help <subcommand>' for help on a specific subcommand.
Available subcommands:
...
[django_extensions]
admin_generator
clean_pyc
clear_cache
compile_pyc
create_app
create_command
create_jobs
create_template_tags
describe_form
drop_test_database
dumpscript
export_emails
find_template
generate_secret_key
graph_models
mail_debug
notes
passwd
pipchecker
print_settings
print_user_for_session
reset_db
runjob
runjobs
runprofileserver
runscript
runserver_plus
set_default_site
set_fake_emails
set_fake_passwords
shell_plus
show_template_tags
show_templatetags
show_urls
sqlcreate
sqldiff
sqldsn
sync_s3
syncdata
unreferenced_files
update_permissions
validate_templates
...
This time, we will use shell_plus
, which is an extension of the shell
command.
By adding the --print-sql
option at startup, you can also see the SQL statement at the time of instance acquisition, so let's add it.
$ ./manage.py shell_plus --print-sql
# Shell Plus Model Imports
from django.contrib.admin.models import LogEntry
from django.contrib.auth.models import Group, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.models import Session
from polls.models import Choice, Question
# Shell Plus Django Imports
from django.db import transaction
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.core.cache import cache
from django.db.models import Avg, Count, F, Max, Min, Sum, Q, Prefetch, Case, When
from django.conf import settings
Python 3.5.1 (default, Jan 23 2016, 02:16:23)
Type "copyright", "credits" or "license" for more information.
IPython 4.2.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]:
In shell, I had to manually import the Model I wanted to use, shell_plus will automatically load the model when you start it.
Let's execute the get_published_data
command created earlier.
In [1]: Question.get_published_data()
Out[1]: SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."pub_date" <= '2016-06-23 06:45:46.716854' ORDER BY "polls_question"."pub_date" DESC LIMIT 21
Execution time: 0.001740s [Database: default]
[<Question:Second question>, <Question: what's up?>, <Question:Third question>]
In [2]:
This time, the --print-sql
option is added, so you can check the SQL statement executed in this way.
There are two ways to register data, one is from the Manager (queryset) and the other is to directly operate the instance.
Let's try from create
first.
Since it is necessary to put the date in pub_date, import django.utils.timezone
in advance and specify today's date and time in pud_date.
In [3]: from django.utils import timezone
In [4]: Question.objects.create(pub_date=timezone.now())
BEGIN
Execution time: 0.000028s [Database: default]
INSERT INTO "polls_question" ("question_text", "pub_date") VALUES ('', '2016-06-23 07:02:01.013534')
Execution time: 0.000638s [Database: default]
Out[4]: <Question: >
This will register a new record in the DB.
In [5]: Question.objects.count()
SELECT COUNT(*) AS "__count" FROM "polls_question"
Execution time: 0.000154s [Database: default]
Out[5]: 4
Next, let's try creating from an instance. In the case of an instance, just creating it will not be reflected in the DB record. By executing save () of the instance, if the record exists, it will be updated, and if it does not exist, it will be inserted.
In [6]: ins = Question(pub_date=timezone.now(), question_text='Create from instance')
In [7]: ins
Out[7]: <Question:Create from instance>
In [8]: Question.objects.count()
SELECT COUNT(*) AS "__count" FROM "polls_question"
Execution time: 0.000177s [Database: default]
Out[8]: 4 #←←←←←←← Not made yet at this point.
In [9]: ins.save() #←←←←←← Record update method execution here
BEGIN
Execution time: 0.000032s [Database: default]
INSERT INTO "polls_question" ("question_text", "pub_date") VALUES ('Create from instance', '2016-06-23 07:07:46.485479')
Execution time: 0.001240s [Database: default]
In [10]: Question.objects.count()
SELECT COUNT(*) AS "__count" FROM "polls_question"
Execution time: 0.000167s [Database: default]
Out[10]: 5 #←←←←←←← inserted
After registering some data, let's try various things by getting records.
There are several methods, but for the time being, there is no problem if you remember filter
and ʻexclude` to narrow down by conditions.
The filter will leave records that match the conditions.
Exclude is the opposite, and records that do not match the conditions will remain.
Pass field name = condition
as an argument of filter.
By adding a character such as __lte
after the field name, it is not an exact match, and the conditions can be changed as follows.
Please refer to Official Document for what conditions can be used.
Finally, the method of specifying the OR condition is described.
Since all filters (exclude) are AND, use the Q class to perform an OR search for conditions.
Q creates an instance with field name = condition
as specified by filter.
OR search can be realized by passing the conditions created there to the filter of QuerySet.
For Q, the logical symbols &
(and) |
(or) ~
(not) can be used.
In [12]: from django.db.models import Q
In [13]: q1 = Q(pk=1)
In [14]: q2 = Q(pk=2)
In [15]: Question.objects.filter(q1|q2)
Out[15]: SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE ("polls_question"."id" = 1 OR "polls_question"."id" = 2) ORDER BY "polls_question"."pub_date" DESC LIMIT 21
Execution time: 0.000390s [Database: default]
[<Question:Second question>, <Question: what's up?>]
Recommended Posts