Last time, I created a Django environment with Docker and tried it easily, so this time I will dig a little deeper into Django through the official tutorial while also serving as a supplement to the previous time. This time, I'm PU from 1 to 3 of the tutorial, where I personally felt it was important.
ʻInclude () is a function that represents a reference to a URLconf (here url.py). Set the path with ʻurls.py
in the argument and the name of ʻurl.py to be referenced in the second argument. Since we want to refer to polls' ʻurls.py
here, we will set polls.urls
.
You can set four arguments for path ()
: route, view, kwargs, and name
.
route
will be the route address for that application.
view
specifies the view function
you want to call.
kwargs
can pass any keyword as a dictionary to view
, it is optional.
name
is a global URL that can be referenced from anywhere in Django if you specify it in ʻURL`.
Let's look at these two examples.
path('polls/', include('polls.urls'))
For example, in this description, route
becomes the part of'polls /'
, and view
becomes include ('polls.urls')
.
Was there a view
in ʻinclude ('polls.urls')? If you think so, take a look at ʻurls.py
again. You can see that it is imported at the beginning.
By the way, view
in the second argument can also take the output of ʻas_views in addition to ʻinclude ()
like this time.
ʻAs_views is a
class only methodthat creates a function that meets the view criteria in Django. For more information, see [Reference article](https://qiita.com/tell-k/items/38c0612a44497b311d6b) and the documentation, but when you chew it, Django first determines the request method called the
dispatch` method and that request method. I have a method that executes a method with the same name as.
In this case, for example, from the previous article
class SampleView(View):
def get(self, request, *args, **kwargs):
return render(request, 'app_folder/page01.html')
def post(self, request, *args, **kwargs):
input_data = request.POST['input_data']
result = SampleDB.objects.filter(sample1=input_data)
result_sample1 = result[0].sample1
result_sample2 = result[0].sample2
context={'result_sample1':result_sample1, 'result_sample2':result_sample2}
return render(request, 'app_folder/page02.html', context=context,)
top_page = SampleView.as_view()
In this part, if request.method =='GET'
is here, SampleView.get () will be executed.
ʻAs_view is instantiated as
cls(when it becomes an instance of a class method, the class itself is represented by
cls instead of
self) and creates a function that executes this
dispatchmethod. Will be
return, which means that it can be taken as an argument because it is classified as
view`.
Well, what is name
?
urlpatterns = [
path('', views.index, name='index')
]
It's this kind of guy.
By doing this, you can make the process that views.index
is called when you hit the URL in the form of root index / index
or root index /
.
There will be a more straightforward way to use it towards the end, so for now just remember that the path is named.
from django.db import models
# Create your models here.
class Question(models.Model):
question_next = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.Foreignkey(Question, on_delete=models.CASCADE) #relation
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
Set in the form of models.Foreignkey (class name of the table to be related, on_delete = models.CASCADE)
.
In this case, the relationship is Question: Choice = 1: multiple
.
To put it simply, there are multiple answers to one question.
ʻOn_deleteThe following sets whether to delete the object associated with it when the referenced object is deleted, or to keep it. In the case of
CASCADE`, it means that all the associated objects are deleted.
For example
# polls_question
id question_next
0 What`s_up?
# polls_choice
id choice_text votes question_id
0 I`m_heading 0 0
1 Stomachache 1 1
Suppose you have a table like this.
Here, the data of ʻid = 0in the
polls_question table is deleted, and the data of ʻid = 0
and ʻid = 1in the
polls_choicetable that refers to it is also deleted. It will be. This is the data deletion process when
CASCADEis specified. There are other options, but it seems that I often use
CASCADE`.
Since it is a little difficult to handle the data in the relation, I interpret that it is better to delete all the data in both tables that are dependent on each other when deleting one.
Of course, you can specify that the other is treated as null, or that only the other is completely deleted.
Well, if you finish up to this point, you can complete the table by migrating with migrate
, but since there is a command that outputs SQL statement
and shows what migrate
is doing, I will try it. ..
python manage.py makemigrations polls
In the case of docker, use the docker exec
command to enter the container, move to the directory containing the manage.py
file, and execute it.
You can save the migrations for that application by specifying the name of the application folder in the makemigrations
command.
For example, if you execute this command this time
Migrations for 'polls':
polls/migrations/0001_initial.py
- Create model Question
- Create model Choice
Thus the name 0001 is given to the migration and the file is saved.
By the way, this time I made a mistake in migrating, and I made a mistake in the properties of the data registered in the table, so I changed it and did make migrations
again.
In that case, the following file 0002_auto_20200408_1848.py
will be created and the table will be updated when migrate
again.
0002_auto_20200408_1848.py
# Generated by Django 3.0 on 2020-04-08 18:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('polls', '0001_initial'),
]
operations = [
migrations.RenameField(
model_name='question',
old_name='question_next',
new_name='question_text',
),
]
Next, let's see what happens when this migration is migrate
.
py manage.py sqlmigrate polls 0001
If you specify the application folder and migration in the sqlmigrate
command like this,
--
-- Create model Question
--
CREATE TABLE `polls_question` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `question_next` varchar(200) NOT NULL, `pub_date` datetime(6) NOT NULL);
--
-- Create model Choice
--
CREATE TABLE `polls_choice` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `choice_text` varchar(200) NOT NULL, `votes` integer NOT NULL, `question_id` integer NOT NULL);
ALTER TABLE `polls_choice` ADD CONSTRAINT `polls_choice_question_id_c5b4b260_fk_polls_question_id` FOREIGN KEY (`question_id`) REFERENCES `polls_question` (`id`);
The SQL statement is displayed like this, and you can see what the migrate
command is doing.
It's easy to see what kind of SQL statement is used to create a table.
After checking so far, let's make a table by migrate
.
Let's try the shell
command as per the tutorial and interact with the database.
#Import the model and get the class
>>> from polls.models import Choice, Question # Import the model classes we just wrote.
#Get all objects of Question class. Since there is no data, the result is empty.
>>> Question.objects.all()
OUT:<QuerySet []>
#To use the datetime module, django.Import the timezone class in it from utils.
>>> from django.utils import timezone
#Assign data to variables in the form of objects
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
#Save data
>>> q.save()
#Get data properties
>>> q.id
1
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)
#Try to change properties
>>> q.question_text = "What's up?"
>>> q.save()
#Failure
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
Rewrite models.py
to avoid failure.
import datetime
from django.db import models
from django.utils import timezone
# Create your models here.
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
#Add 1
def __str__(self):
return self.question_text
#Add 2
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
#Add 3
def __str__(self):
return self.choice_text
__str__ ()
is an object method.
For example, here it is responsible for setting self
as an argument to instrument the class and make each property human readable.
The reason the object didn't display well in Question.objects.all ()
earlier is that the object remained machine readable.
In other words, the object looks like an object of this class
to our eyes as it is, so we will give it a name.
Addition 2 is the original method.
Since it is a mathematical expression after return, it will return True or False.
Since timezone.now ()
is the current date and datetime.timedelta (days = 1)
is one day later, today--tomorrow
is yesterday, and more than that, self.pub_date
is true. ..
In other words, it's a method that returns True
if the data in the Question table was created after yesterday.
However, as it is, there is a bug that True
is returned even if it is a so-called future date.
The Question table means registering the content of the question, but asking a question must always be past or present in terms of time.
You asked yesterday, you said you asked today, but you didn't say you asked tomorrow, right?
This bug seems to be fixed in the items after 4 of the tutorial, so I will leave it as it is for the time being.
Now, run the shell command again.
>>> from polls.models import Choice, Question
# __str__Since I added the method, question of Question property_text is called as the name of the object.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
#Confirm that you can narrow down the objects by id with the filter method.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
#Question in the same way_Try to narrow down the objects whose text starts with what.
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
#Get objects added this year
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
# id=Error because the data of 2 does not exist
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.
#Get an object with a primary key of 1. Usually the primary key is id. So here pk=1 == id=Become 1
>>> Question.objects.get(pk=1)
<Question: What's up?>
#Check the operation of the additional 2 methods
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
#Instantiate an object with a primary key of 1.
>>> q = Question.objects.get(pk=1)
#Call the choice object associated with the above objects. The point is how many answers to the question. Since there is no data in the Choice table, it is returned empty.
>>> q.choice_set.all()
<QuerySet []>
# Question.objects.get(pk=1)As an answer linked to, add three data to the choice table.
>>> 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)
#The question property is the foreign key of the Choice table, that is, the primary key reference of the Question table.
>>> c.question
#Therefore, as mentioned above, the c variable is q= Question.objects.get(pk=1)Since it is tied to, the following result is returned.
<Question: What's up?>
# q = Question.objects.get(pk=1)Call the Choice object associated with and count the number of objects
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
#Among the Choice objects, narrow down the objects whose question creation date associated with the primary key of the referenced Question table is this year.
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
#Delete a specific Choice object.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
polls/views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import Question
# Create your views here.
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {
'latest_question_list': latest_question_list,
}
return render(request, 'polls/index.html', context)
def detail(request, question_id):
"""
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detaill.html', {'question': question})
"""
#The above is a Django shortcut. get_object_or_404 method
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
def results(request, question_id):
response = "You`re looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You`re voting on question %s." question_id)
polls/urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', vies.vote, name='vote'),
]
I mentioned path ()
earlier.
For example, suppose you have access to / polls / 34 /
here.
Do you remember what happens with the above settings?
The correct answer is to refer to ʻurl patterns in ʻurls.py
to determine which view function to send the request to, and then execute the view function called there.
Let's dig a little deeper.
First, look at the part specified in the first argument of path ()
. I did earlier that the root address is entered in this part.
For example, the first description is easy to understand. In this way, the URL pattern specified here is / polls /
.
In other words, you can see that polls /
is automatically specified even if the first argument is empty. (If not specified, an error will occur.)
This means that when Django receives a request, it first loads the ʻurls.py file specified in
ROOT_URLCONFof
settings.py. This is a file that designs the URL of the entire system. In the previous example, that is
project / project / urls.py`.
Let's say there is such a description there.
urlpatterns = [
#URL to access the management site
path('admin/', admin.site.urls),
#URL to access the application "app" created this time
path('app/', include('app.urls')),
#If you do not specify any URL
path('', views.index, name='index'),
path('polls/', include('polls.urls')),
]
Some people may have already understood the mechanism when they came to this point.
Think of the first path ()
part as a spell. The third description is the same as before.
Of note are the second and fourth descriptions.
By writing like this here, you can refer to ʻurls.py of each application, and you can match the request with the variable ʻurl patterns
.
Also, if you write path ('polls /', include ('polls.urls'))
here, there is a request such as / polls / 34 /
in polls / url.py
. , It is already judged that polls
matches, and it is judged which path () the other part matches.
In that case, / polls / 34 /
will not match the first path ()
of polls / url.py
.
Then, what happens with the second and subsequent descriptions, let's look at <int: question_id>
.
This means looking for ʻintin the URL pattern and naming it
question_id`.
From the document
With angle brackets, part of the URL is "captured" and sent to the view function as a keyword argument. The: question_id> part of the string defines the name used to identify the matching pattern, and the <int: part is the converter that determines the pattern that matches this part of the URL path.
In this case, the access / polls / 34 /
will leave the string 34 /
, so it will match with the second path ()
.
After that, the request is passed to the specified view function.
In this case, the request is passed to the detail ()
method of polls / views.py
.
Looking at it, request
and question_id
are specified as arguments, so as a result
detail(request=<HttpRequest object>, question_id=34)
Arguments are passed to detail ()
, and the method is executed.
We will check later what kind of processing is actually done.
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
This part of settings.py
is the key, first specify where to put the template html in DIRS.
If you specify a template for only one application, the application folder name will be entered instead of BASE_DIR
, but after setting BASE_DIR
and then setting ʻAPP_DIRS to
True`, the subdirectory of the folder specified above will be the application. You can set it to search by the name of the application folder and apply the template.
In other words, in this case, the application folder name is polls
, so the directory where the templates are saved is templates / polls
.
The render ()
method can specify a request as the first argument, a template name as the second argument, and a dictionaryas the third argument. Then, it has the role of creating a
HttpResponseobject based on the received arguments and returning it. It seems that this kind of processing is called rendering. Rendering is an image of processing that expresses certain information by changing it into a form. In this case, information such as
request objects (
sessionand
get, post) and
context` (database data). And the template html file are combined well,
It seems that it is processing like molding as a browser screen.
Also, by importing the render
class, you don't have to import the loader
class or the HttpResponse
class.
However, the HttpResponse
class has other roles, so it seems safe to keep it.
The next exception is.
from django.http import Http404
from django.shortcuts import render
from .models import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detail.html', {'question': question})
This is a familiar form of the so-called try-catch
.
You can get a 404 error by importing the Http404
class from the django.http
module.
The code that raises an exception depending on whether or not the question
object is assigned to the question
variable.
To make it a little easier to understand, if the data with the requested ID does not exist, an exception will be issued.
As with the render ()
method, which also played the role of loader
and HttpRespose
, you can import more concisely written methods from the django.shortcuts
module.
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
By specifying the model name and key in the get_object_or_404 ()
method, try-catch
will be processed automatically.
You can set multiple keys, for example, if you specify get_object_or_404 (User, pk = 1, name ='tanaka')
, the primary key is 1 and the name key value is tanaka. You will get a 404 error.
By the way, there is also a method called get_list_or_404 ()
.
It is a method to get the value from the model in the same way, but this time, based on the specified key, narrow down with the filter () method and get all with list.
For example, if you specify get_list_or_404 (User, pk = 1, name ='tanaka')
as before, will you get all the data with primary key 1 and name
key value tanaka
and return it as a list? If none of them exist, you will get a 404 error.
It is a template system that was also Laravel, but there is not much difference in writing and usage.
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
For example, with this description, you may have noticed it just by looking at it, but if it is {{}}
and double curly braces, it will be a variable expression.
In other words, it represents the attributes of the variable, and it says that the question_text
part of the Question
object will be included here.
As a process
From the document
The template system uses dot search syntax to access the attributes of variables. Taking {{question.question_text}} as an example, Django first does a dictionary search on the question object. If that fails, it will now search as an attribute and in this case it will succeed. If the attribute search fails, the list index will be searched.
It means that.
As you can see from the creation of this tutorial, the content of the question is included in the h1
tag.
Therefore, there are multiple questions, and it is difficult to create a template each time ... So it is OK if you write this way and understand that the contents of the h1
tag will change depending on the content of the question. ..
Next, in the case of one curly brace called {}
. This is making a method call.
The processing this time is as follows.
From the document
The method call is done inside a {% for%} loop. question.choice_set.all is interpreted as question.choice_set.all () in Python code. As a result, it returns an iterable object consisting of Choice objects, which can be used with the {% for%} tag.
In other words, you can list as many li
tags as there are choice.choice_text
objects obtained by this method.
In this case, it is an expression of the answer to the content of the question.
There are multiple ways to answer one question, so it's okay if you know that it is expressed in this way.
Also, remember, for example, that you specified name
earlier in polls.urls
?
path('<int:question_id>/', views.detail, name='detail'),
It's like this. If you specify it like this, you don't need to write the URL path by using the url tag in the template. For example
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
It is such a description.
Normally, you have to specify ʻURL or path in the
href attribute of the ʻa
tag.
However, I have defined in advance that detail
is the name of the path / polls /'<int: question_id> /'
.
So, if you write detail
as above and then put ʻint type named
question_id(question.id in this case), it will be a path. As an aside, if you want this path to look like
polls / specifics / 12 /, edit
polls / urls.py`.
Finally, the namespace.
By defining a namespace in ʻurls.py of each application folder in advance, it will automatically determine which application's ʻURLconf
should be used when managing multiple application folders in one project. I will.
polls/urls.py
#Add the following
app_name = 'polls'
Specify like this. And on the template side, in the part of specifying the name of the URL path
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
I will specify it with a namespace like this.
This means that the polls app's detail view path
goes here.
What is rendering in the first place? [Easy-to-understand explanation for beginners] What is Django's class-based view as_view?
Recommended Posts