Skip to content

Instantly share code, notes, and snippets.

@dohsimpson
Last active October 26, 2018 15:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dohsimpson/b72561fec22aab21c77e to your computer and use it in GitHub Desktop.
Save dohsimpson/b72561fec22aab21c77e to your computer and use it in GitHub Desktop.
Learn Django
Django
Model:
Class Based Model:
* class MODEL(models.Model):
FIELD1 = models.AFIELD([FIELDNAME], [default=VALUE])
def __str__(self):
...
Fields:
MODEL_LOWERCASE = ForeignKey(MODEL, on_delete=models.CASCADE, [verbose_name=NAME])
MODEL_LOWERCASE_PLURAL = ManyToManyField(MODEL, [through="MODEL2"])
MODEL_LOWERCASE = OneToOneField(MODEL, [parent_link=BOOL]): parent_link=True is set for multi-table inheritance automatically
Class Meta:
abstract = BOOL
proxy = BOOL
managed = BOOL: if False, DB table for this model will not be created or deleted by Django.
app_label = APP_NAME: not sure why you would ever need this
ordering = "+-FIELD"
db_table = DB_NAME
indexes = [MODEL_INDEXs]
unique_together = ((FIELDs)): The ValidationError raised during model validation when the constraint is violated has the unique_together error code
constraints = [CONSTRAINTS]
verbose_name
verbose_name_plural
Class Attributes:
objects: the default Model Manager, the interface for SELECT
Instance Methods:
[@property]
def METHOD(self):
return STUFF
def __str__(self):
return STRING
def get_absolute_url(self): Any object that has a URL that uniquely identifies it should define this method.
return STRING
# DB related:
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
Inheritance:
3 Styles:
1. Often, you will just want to use the parent class to hold information that you don’t want to have to type out for each child model. This class isn’t going to ever be used in isolation, so Abstract base classes are what you’re after.
2. If you’re subclassing an existing (concrete) model (perhaps something from another application entirely) and want each model to have its own database table, Multi-table inheritance is the way to go.
3. Finally, if you only want to modify the Python-level behavior of a model, without changing the models fields in any way, you can use Proxy models. (useful for add method, Manager, ordering, etc.
Model CLI API:
* MODEL.objects....: to access MODEL table
Admin Interface HTML:
* admin.py: admin.site.register(models.MODEL)
-> Useful for NewsRoom, for SaaS, maybe not so much
* class QuestionAdmin(admin.ModelAdmin):
fields = ['pub_date', 'question_text']
OR
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
admin.site.register(Question, QuestionAdmin)
Relations:
Many-to-one: use on MANY: ForeignKey(ONE)
Many-to-Many: use ManyToManyField(MODEL)
Many-to-Many with Extra Fields on Relationship: use ManyToManyField(MODEL, through="INTERMEDIATE_MODEL") and define INTERMEDIATE_MODEL with two ForeignKey on the two MODELs.
One-to-one: use OneToOneField(ONE)
Routing:
URLconf list:
* urls.py: urlpatterns = [
path('URL', VIEW[, name=NAME]),
path('<int:PARAM>/',...),
...
]
* Tried sequentially in order, if none matches, return 404 page
* views are called with request object AND parameters (GET?)
* path()
- route: string: URL pattern
- view: function: VIEW(HttpRequest, args)
- kwargs: other arguments if you wish to pass to view
- name: refer to your URL globally, useful in templates
* name:
app_name = 'polls': this gives "name" namespace. In template, can be used as href="{% url 'polls:detail' question.id %}"
View:
Return Value:
* HttpResponse object
* HttpResponseRedirect(url)
* reverse(NAME, [args=(ARGS,)]): returns URL string
* OR an Exception (e.g. Http404)
Function based View
* def FUNCTION(request, params):
[filter db]
[context = ...]
return render(request, TEMPLATE_PATH: string, context)
That code loads the template called polls/index.html and passes it a context. The context is a dictionary mapping template variable names to Python objects.
* def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
* get_object_or_404(Model, FIELD=VALUE): FIELD is any valid search term: e.g. pk. Calls .get()
* get_list_or_404(Model, FILTER=VALUE): Calls .filter()
* request.POST, request.GET: dict with form values
* request.method: stores the HTTP method as string: "GET", "POST", etc.
Class based View
* Generic View
* ListView
- class XXX(generic.ListView):
model = MODEL
[template_name = 'polls/index.html'] OR default to 'APP/MODEL_list.html'
[context_object_name = 'latest_question_list'] OR default to 'model.lowercased_list'
- URLconf: path(<int: pk>/PATH, view.XXX.asView, ARGS): NOTE: parameter must be called pk
- template: context uses model name lower cased: e.g. model = polls.Questions -> {{ question }}
* DetailView
- class XXX(generic.DetailView):
model = MODEL
[template_name = 'polls/results.html'] OR default to 'APP/MODEL_detail.html'
[context_object_name = 'latest_question_list'] OR default to 'model.lowercased'
def get_queryset(self):
return Question.objects.order_by('-pub_date')[:5]
- URLconf: path(<int: pk>/PATH, view.XXX.asView, ARGS): NOTE: parameter must be called pk
- template: context uses model name lower cased: e.g. model = polls.Questions -> {{ question }}
Template
* mysite/news/templates/news/year_archive.html
{% extends "base.html" %}
{% block title %}Articles for {{ year }}{% endblock %}
{% block content %}
<h1>Articles for {{ year }}</h1>
{% for article in article_list %}
<p>{{ article.headline }}</p>
<p>By {{ article.reporter.full_name }}</p>
<p>Published {{ article.pub_date|date:"F j, Y" }}</p>
{% endfor %}
{% endblock %}
* mysite/templates/base.html
{% load static %}
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<img src="{% static "images/sitelogo.png" %}" alt="Logo">
{% block content %}{% endblock %}
</body>
</html>
* use view:
href="{% url 'detail' question.id %}"
* method call:
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
Static Files:
* In template:
href="{% static 'polls/style.css' %}"
Django Directory Structure
* project: django-admin startproject mysite
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
* App: python manage.py startapp polls
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
Settings:
* DB:
DATABASES = {
ENGINE = Either 'django.db.backends.sqlite3', 'django.db.backends.postgresql',
'django.db.backends.mysql', or 'django.db.backends.oracle'. or others.
NAME: Name of your database,
[if not using SQlite:]
USER,
PASSWORD,
HOST
}
* TIME_ZONE:
set to your timezone
* INSTALLED_APPS:
[by default:]
• django.contrib.admin – The admin site. You’ll use it shortly.
• django.contrib.auth – An authentication system.
• django.contrib.contenttypes – A framework for content types.
• django.contrib.sessions – A session framework.
• django.contrib.messages – A messaging framework.
• django.contrib.staticfiles – A framework for managing static files.
[your apps:]
IMPORT FROM PROJECT ROOT
manage.py
* DB:
* makemigrations: create polls/migrations/DDDD_XXXXXX.py. Is humanly readable.
* python manage.py migrate
The migrate command looks at the INSTALLED_APPS setting and creates any necessary database tables according to the database settings in your mysite/settings.py file and the database migrations shipped with the app
If you’re interested, run the command-line client for your database and type \dt (PostgreSQL), SHOW TABLES; (MySQL), .schema (SQLite), or SELECT TABLE_NAME FROM USER_TABLES; (Oracle) to display the tables Django created.
* python manage.py sqlmigrate polls DDDD
This shows you the SQL statement to run the migration for DDDD
Meta:
Passing Values:
To Views:
in template: {% url "NAME" ARGS... %}
In View: HttpResponseRedirect(reverse(NAME, args=(ARGS...))
To Templates:
In View: render(request, TEMPLATE, context): context is a dict containing variables, which could be QuerySet
Accessing Arguments:
In Views:
GET:
request.GET[KEY] or KeyError
POST:
request.POST[KEY] or KeyError
In Templates:
{{ KEY }}
{{ A.B.C }}}: C could be method, B and C could be attribute, key, index, etc.
Ceveats:
* The outer 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.
* Development Server Auto-reload: However, some actions like adding files don’t trigger a restart, so you’ll have to restart the server in these cases.
* The include() function allows referencing other URLconfs. Whenever Django encounters include(), it chops off whatever part of the URL matched up to that point and sends the remaining string to the included URLconf for further processing.
* You should always use include() when you include other URL patterns. admin.site.urls is the only exception to this.
* Also make sure that the database user provided in mysite/settings.py has “create database” privileges. This allows automatic creation of a test database which will be needed in a later tutorial.
* Some Field classes have required arguments. CharField, for example, requires that you give it a max_length. That’s used not only in the database schema, but in validation, as we’ll soon see.
* To include the app in our project, we need to add a reference to its configuration class in the INSTALLED_APPS setting.
* We’re using this instead of simply typing “python”, because manage.py sets the DJANGO_SETTINGS_MODULE environment variable, which gives Django the Python import path to your mysite/settings.py file.
* 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.
* It’s important to add __str__() methods to your models, not only for your own convenience when dealing with the interactive prompt, but also because objects’ representations are used throughout Django’s automatically-generated admin.
* When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zoneaware datetime objects internally, and translates them to the end user’s time zone in templates and forms.
* Since translation is turned on by default, the login screen may be displayed in your own language, depending on your browser’s settings and if Django has a translation for this language.
* A view is a “type” of Web page in your Django application that generally serves a specific function and has a specific template.
* Your project’s TEMPLATES setting describes how Django will load and render templates. The default settings file configures a DjangoTemplates backend whose APP_DIRS option is set to True. By convention DjangoTemplates looks for a “templates” subdirectory in each of the INSTALLED_APPS.
* (by defining "name", )If you want to change the URL of the polls detail view to something else, perhaps to something like polls/ specifics/12/ instead of doing it in the template (or templates) you would change it in polls/urls.py
* The template system uses dot-lookup syntax to access variable attributes. In the example of {{ question.question_text }}, first Django does a dictionary lookup on the object question. Failing that, it tries an attribute lookup – which works, in this case. If attribute lookup had failed, it would’ve tried a list-index lookup. (and method call)
* In short, all POST forms that are targeted at internal URLs should use the {% csrf_token %} template tag.
* As the Python comment above points out, you should always return an HttpResponseRedirect after successfully dealing with POST data. This tip isn’t specific to Django; it’s just goodWeb development practice.
* Avoid race-condition on DB PUT: Another useful benefit of F() is that having the database - rather than Python - update a field’s value avoids a race condition.
* That’s what django.contrib.staticfiles is for: it collects static files from each of your applications (and any other places you specify) into a single location that can easily be served in production.
* Of course the {% static %} template tag is not available for use in static files like your stylesheet which aren’t generated by Django. You should always use relative paths to link your static files between each other, because then you can change STATIC_URL (used by the static template tag to generate its URLs) without having to modify a bunch of paths in your static files as well.
* Astute readers will ask: But if DIRS was empty by default, how was Django finding the default admin templates? The answer is that, since APP_DIRS is set to True, Django automatically looks for a templates/ subdirectory within each application package, for use as a fallback (don’t forget that django.contrib.admin is an application)
* Generally, each model maps to a single database table.
* Each attribute of the model represents a database field.
* Be careful not to choose field names that conflict with the models API like clean, save, or delete.
* field class types is used to determine the default HTML widget to use when rendering a form field (e.g. <input type="text">, <select>) and The minimal validation requirements, used in Django’s admin and in automatically-generated forms.
* By default, Django gives each model the following field: id = models.AutoField(primary_key=True) If you’d like to specify a custom primary key, just specify primary_key=True on one of your fields. If Django sees you’ve explicitly set Field.primary_key, it won’t add the automatic id column.
* To save you time, Django automatically derives the name of the database table from the name of your model class and the app that contains it. A model’s database table name is constructed by joining the model’s “app label” – the name you used in manage.py startapp–to themodel’s classname,with an underscore between them. Use class Meta: db_table = XXX to override
* If you reference User directly (for example, by referring to it in a foreign key), your code will not work in projects where the AUTH_USER_MODEL setting has been changed to a different user model. Instead of referring to User directly, you should reference the user model using django.contrib.auth.get_user_model(). This method will return the currently active user model – the custom user model if one is specified, or User otherwise. (or settings.AUTH_USER_MODEL)
* You can also create recursive relationships (an object with a many-to-one relationship to itself)
* It’s suggested, but not required, that the name of a ForeignKey field (manufacturer in the example above) be the name of the model, lowercase. You can, of course, call the field whatever you want.
* It doesn’t matter which model has the ManyToManyField, but you should only put it in one of the models – not both. Generally, ManyToManyField instances should go in the object that’s going to be edited on a form
* If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source mode name, lowercased. This Manager returns QuerySets
* One difference is in the attribute naming: The model that defines the ManyToManyField uses the attribute name of that field itself, whereas the “reverse” model uses the lowercased model name of the original model, plus '_set' (just like reverse one-to-many relationships).
* A field name cannot end with an underscore
* A ManyToManyField cannot be included in unique_together. (It’s not clear what that would even mean!) If you need to validate uniqueness related to a ManyToManyField, try using a signal or an explicit through model.
* If no custom Manager is defined, the default name is objects. Managers are only accessible via model classes, not the model instances.
* Model Method is a valuable technique for keeping business logic in one place – the model.
* Overridden model methods are not called on bulk operations Note that the delete() method for an object is not necessarily called when deleting objects in bulk using a QuerySet or as a result of a cascading delete. To ensure customized delete logic gets executed, you can use pre_delete and/or post_delete signals. Unfortunately, there isn’t a workaround when creating or updating objects in bulk, since none of save(), pre_save, and post_save are called.
* Fields inherited from abstract base classes can be overridden with another field or value, or be removed with None.
* Multi-table Inheritance (Inheriting a concrete Model class) introduces links between the child model and each of its parents (via an automatically-created OneToOneField).
* So a child model does not have access to its parent’s Meta class. However, there are a few limited cases where the child inherits behavior from the parent: if the child does not specify an ordering attribute or a get_latest_by attribute, it will inherit these from its parent.
* Proxy models inherit Meta attributes in the same way as regular models.
* For multi inheritance: The first base class that a particular name (e.g. Meta) appears in will be the one that is used; for example, this means that if multiple parents contain a Meta class, only the first one is going to be used, and all others will be ignored.
* Generally, you won’t need to inherit from multiple parents. The main use-case where this is useful is for “mix-in” classes: adding a particular extra field or method to every class that inherits the mix-in
* Field name “hiding” is not permitted. Django will raise a FieldError if you override any model field in any ancestor model.
* Field name hiding restriction doesn’t apply to model fields inherited from an abstract model. Such fields may be overridden with another field or value, or be removed by setting field_name = None.
* instantiating a model in no way touches your database; for that, you need to save().
* The combo of the two is so frequent because typically if you're going to allow a field to be blank in your form, you're going to also need your database to allow NULL values for that field. The exception is CharFields and TextFields, which in Django are never saved as NULL. Blank values are stored in the DB as an empty string ('')
* Such values (Error Handler, e.g. handler404) can be set in your root URLconf. Setting these variables in any other URLconf will have no effect. Values must be callables, or strings representing the full Python import path to the view that should be called to handle the error condition at hand
FAQ:
* [Django-REST-Framework] curl token authentication 403 even though provided token: have you set the DEFAULT_AUTHENTICATION_CLASSES in your setting to include TokenAuthentication? See Rest Framework API Guide on Authentication.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment