# Django 3 by Example - Chapter 1
---
### Models
```python=
from django.db import models
# Why import user ?
from django.contrib.auth.models import User
class Post(models.Model):
STATUS_CHOICES = (
# Why keep multiple values in a tuple,
# When only one is shown in admin ?
('draft', 'DRAFT'),
('published', 'Published'),
)
title = models.CharField(max_length=250) # CharField -> Varchar
slug = models.SlugField(max_length=250, unique_for_date='publish')
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='blog_posts')
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
# You can pass custon choices to user.
status = models.CharField(max_length=10, choices=STATUS_CHOICES)
class Meta:
"""
Storing MetaData
"""
ordering = ('-publish',)
```
* `author` has the `many-to-one` relationship with the `User`. i.e each post is written by a user and a user can write many posts.
* `User` is the User Model from the builtin auth application.
* `auto_now_add` saves the date automatically when creating an object.
* `auto_now` date will be updated automatically when saving an object.
* Django automatically creates a primary key for each models, the default primary key is the `id` field which is an interger.
* To override the primary key for any models we could use `primary_key = True`
```python=
from django.db import models
class Fruit(models.Model):
name = models.CharField(max_length=100, primary_key=True)
```
* Primary key fields are read only
* If you change the value of the of an existing primary key and save it. Two objects will be created alongside.
```python=
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
<QuerySet ['Apple', 'Pear']>
```
### Querry Set and Managers
* <i>Query Set</i> is a collection of database queries.
* `.filter()`, `exclude()`, `order_by()` are some of the common functions used to narrow down the result based on given parameters.
```python=
# Returns a query set of all the objects for the model Post
Post.objects.all()
# Returns a query set all the objects for the model Post that were
# published in 2020
Post.objects.filter(publish__year=2020)
```
* Django's querysets are lazy, changes you make on the objects are only reflected in the DB if you call `save()` method.
```python=
post.title = 'New Name'
post.save()
```
* Deleting objects will also delete any dependent relationships for ForeignKey objects defined with `on_delete` set to `CASCADE`.
* Each django models has at least one manager by default which is called the `objects`.
* You get the queryset object using your model manager.
```python=
class PublishedManager(models.Manager):
"""
Custom Model Manager
"""
def get_queryset(self):
"""
Overide's the get_queryset()
to only return posts that have been published.
"""
return super().get_queryset().filter(status='published')
```
* To use the `PublishedManager` you need to add it to the model class.
```python=
class Post(models.Model):
<supressed code>
published = PublishedManager()
```
* Now you can get the query set by calling the `published` manager instead of the default one `objects`
```python=
>>> from blog.models import Post
>>> Post.published.filter(title__startswith='Who')
```
### Views
* A view is a “type” of Web page in your Django application that generally serves a specific function and has a specific template.
```python=
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_list(request):
"""List all the posts in the DB.
"""
posts = Post.published.all()
context = {'posts': posts}
return render(request,“blog/post/list.html',
context)
```
* `render()` function takes in three arguements.The request object as its first argument, a template name as its second argument and a dictionary(context) as its optional third argument.
* The context is a dictionary mapping template variable names to Python objects.
* Same context is used when it's being used in the template, so it's a better to return the dict as it is where `posts` can be directly used.
```python=
return render(request,
'blog/post/list.html',
{'page': page,
'posts': posts})
```
```python=
{% block content %}
{% for post in posts %}
{% endfor %}
{% endblock %}
```
```python=
def post_detail(request, year, month, day, post):
"""
Display detail of a single post.
"""
post = get_object_or_404(
Post,
slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day
)
return render(request,
'blog/post/detail.html',
{'post': post})
```
* `get_object_or_404()` function takes a Django model as its first argument and an arbitrary number of keyword arguments, which it passes to the get() function of the model’s manager. It raises Http404 if the object doesn’t exist.
#### Class Based Views
* All the class base views inherit from the `View` class which handels `HTTP method dispatching` and other features.
* CBV has seprate methods for handeling HTTP Verbs.
```python=
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('Response to GET request')
def post(self, request, *args, **kwargs):
return HttpResponse('Response to POST request')
def delete(self, request, *args, **kwargs):
return HttpResponse('Response to DELETE request')
```
* `mixins ` help in creating resuable views.
* Built in class-based generic views have some of common view functions already implemented. These can be directly imported `from django.views.generic import ListView`
```python=
class PostListView(ListView):
queryset = Post.published.all()
context_object_name = 'posts'
paginate_by = 3
template_name = 'blog/post/list.html'
```
* `def post_list(request)` and `class PostListView(ListView):` are similar in functionality.
* CBV are added to the urlconf in the following manner
```python=
path('', views.PostListView.as_view(), name='post_list')
```
### URLs
#### How does django URLs are mapped inside a django app ?
```
url: http://pradhvanbisht.in/blog/2020/4/16/pycon2020/
```
* When somebody requests a page from your website.Django will load the python module whatever is inside the `ROOT_URLCONF =` (generally <your_project_name>.urls).
* It finds the variable named `urlpatterns` and traverses the patterns in order.
* After finding the match at 'blog/', it strips off the matching text ("blog/") and sends the remaining text – "2020/4/16/pycon2020/" – to the ‘blog.urls’ URLconf for further processing.
* There it matches `'<int:year>/<int:month>/<int:day>/<slug:post>/'`, resulting in a call to the `post_detail()` view like`def post_detail(request, year, month, day, post)`
---
* `<>` is used to capture values from the the URL.
* Any value captured inside `<>` is treated a string.
* We need to explictly add the types(`<int:year>`) to return the exptected data type.
* The main project's `urls.py` creates a seprate namespace for every app's url and is added with `include`
```python=
path('blog/', include('blog.urls', namespace='blog'))
```
* The app's urls should also be given a seprate namespace so that it's unique and can also be called in the template the same namespace.
```python=
path('<int:year>/<int:month>/<int:day>/<slug:post>/',
views.post_detail, name='post_detail')
```
#### Cannaonical URLs
* A canonical URL is the preferred URL for a resource.
* A canonical URL is "the official" url to a certain page. Sometimes an asset can be displayed in multiple different URL's, for example:
```python=
/en/shoes/1-nike-shoes/
/en/shoes/1-nike-shoes?sort=price&order=asc
/en/shoes/1-nike-shoes?sort=price&order=desc
```
* Here we have the same asset displayed in 3 different URL's. The "Canonical URL" would be the one we defined as the main one. E.g. `/en/shoes/1-nike-shoes/`.
* The convention in Django is to add a `get_absolute_url()` method to the model that returns the canonical URL for the object.
```python=
class Post(models.Model):
# <supressed code>
def get_absolute_url(self):
return reverse('blog:post_detail',
args=[self.publish.year,
self.publish.month,
self.publish.day, self.slug])
```
* The canonical URL of the Post woul be `http://talkingdjango.io/blog/2020/4/16/pycon2020/`
* `reverse()`
#### Templates
* `TEMPLATES = []` in `settings.py` has a dict with a key `'DIRS'` add the following code to it to make a common templates folder.
```python=
'DIRS': [os.path.join(BASE_DIR, "templates")]
```
* templates can have dirs based on the number of apps.
* `{% tag %}` template tags control the rendering of the template.
* `{{ variable }}` template variables get filled with content when it is rendered.
* `{{variable|filter}}` filters allow you to modify the variables.
```python=
{% for post in posts %}
{{post.title}}
{{ post.body|truncatewords:50|linebreaks }}
```
---
#### Refrence Material
* [Introduction to Django Models](https://docs.djangoproject.com/en/3.0/topics/db/models/)
* [URL Dispatcher](https://docs.djangoproject.com/en/3.0/topics/http/urls/)
* [Model Managers](https://docs.djangoproject.com/en/3.0/topics/db/managers/)