As described in previous post, a model contains the essential fields and behaviors of the data the user is storing. To create a polling app, we will need to create two models: Question and Choice. These concepts are represented by Python classes.
polls/models.py¶
from django.db import models
from django.utils import timezone
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
// if any time utility is needed, use **datetime** package and **django.utils.timezone**
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)
Each model is represented by a class that subclasses django.db.models.Model. The parent class Model takes in *args and **kwargs parameters and loops through each input argument, thereby allowing each model classes to store appropriate values to corresponoding fields when they are instantiated.
Each field is represented by an instance of a Field class (CharField & DateTimeField), which tells Django what type of data each field holds.
Miscellaneous Notes
When declaring fields, various options can be passed as arguments. Below are some commonly used options originally written in (this article).
# null (default False)
# Specify whether or not the field can be null. This is especially useful
# when adding a new field to a model.
age = models.IntegerField(null=True)
# default
# Specify a default value for the field when none is provided.
favorite_color = models.CharField(default='blue')
# primary_key
# When primary_key=True, that field will be used as the primary key
# of the model. Setting this will prevent the automatic id primary
# key field from being used.
ssn = models.CharField(max_length=11, primary_key=True)
# choices
# An iterable of values used to specify the set of valid options
# for a field. Each value should be a tuple of two values, the first
# being the field's value and the second being what is shown to the user (human-readable name).
# ('C', 'Celsius') will store the value 'C' in the database, but
# in HTML it will output 'Celsius'.
CELSIUS = 'C'
FAHRENHEIT = 'F'
KELVIN = 'K'
TEMP_CHOICES = (
(CELSIUS, 'Celsius'),
(FAHRENHEIT, 'Fahrenheit'),
(KELVIN, 'Kelvin')
)
temperature_scale = models.CharField(max_length=1,
choices=TEMP_CHOICES,
default=CELSIUS)
# max_length (default varies by Field type)
# The maximum number of characters of the value for a field.
name = models.CharField(max_length=100)
# auto_now (default False)
# Used for dates and times, will use a datetime class (time, date, or
# datetime) to automatically set the value of the field upon creation.
date = models.DateField(auto_now=True)
date_time = models.DateTimeField(auto_now=True)
time = models.TimeField(auto_now=True)
# validators
# Sometimes you will want the data for a field to be more strict than
# the field type enforces on itself. You can add a list of validators
# to a field that will raise a ValidationError when the conditions
# are not met.
from django.core.exceptions import ValidationError
def validate_color(color):
if color == 'brown':
raise ValidationError('No one\'s favorite color is brown')
favorite_color = models.CharField(validators=[validate_color])
The instantiation arguments can either be index-based (args) or keyword-based (kwargs), thanks to inheriting the Model class as mentioned above.
# kwargs with no included primary key
# auto-increment as nothing is done with the primary key
q1 = Question(question_text="What's up?", pub_date=timezone.now())
# using args will require including the primary key
# "None" is used to allow auto-increment of the default id added by Django
q2 = Question(None, "How are you?", timezone.now())
To invoke the Python shell, use this command:
$ python manage.py shell
This opens up a terminal that has the Django settings already imported, allowing us to work directly from the root folder of a Django project.
what 데이터를 불러올때 다불러놓고 filter 하는것보다 맞게 불러오는게 더 효율적임.
Using the Account model example from the previous post,
from account.models import Account
# The following create() will instantiate and add an account object to the
# database --> result in the database: "account: account object (1)"
Account.objects.create(name='Peter', phone='12341234', email='test@test.net', password='123123')
# The following way initializes the variable a,but does not affect the actual database
# In order to update the database, a.save() command is required
a = Account(name='Peter', phone='213123123', email='test@test.net', password='123123123123')
# The following way updates the database, but does not initialize
# the variable a, as there is no return value
a = Account(name='Peter', phone='213123123', email='test@test.net', password='123123123123').save()
# The following way also updates the database,
# but nothing is printed or returned
Account(name='Peter', phone='213123123', email='test@test.net', password='123123123123').save()
.save() is required every time if you would like to update the database with a new variable.
If no argument is passed to filter(), the return value is the same as all()
c = Account.objects.filter()
To access a specific instance,
c = Account.objects.filter(id=1)
Or,
d = Account.objects.get(id=1)
The difference between these two is that the get() method only takes in and returns a single instance. On the other hand, filter() can return multiple elements in a single queryset.
d = Account.objects.get(name='Peter')
may return an error if multiple elements in the database have same key-value pair of name='Peter' (multiple elements are trying to be accessed and returned, which is not possible for get() method).
If you are looking for just one specific element, it is more efficient to use the get() method, as returning with a queryset requires list iteration to access a specific element.
There are merits to using queryset as well, however. As long as the data is organized in queryset form, Django's built in methods are still applicable; once an element from queryset is instantiated, it can no longer benefit from the Django framework as it is now in the realm of Python.
For example,
c = Account.objects.filter(name='박지훈').first() or last()
# To update,
Account.objects.filter(id=1).update(name='서준표')
# Or, without the update() method,
d.name='김미선'
d.save()
As we can see from above, updating using filter() is more convenient.
To retrieve the entire data set from the database,
a = Account.objects.all()
The returned data are in the form of queryset, containing model instances.
b = Account.objects.values()
values() returns a queryset consisting of dictionaries that represent an object, with the keys corresponding to the attribute names of model objects. This method is highly compatible with json as json is also in a dictionary form.
>>> a[0].name # dot notation for class
"Peter's Account"
>>> b[0]['name'] # bracket notation for dictionary
"Peter's Account"
# This list contains object instances
>>> Account.objects.filter(name__startswith='Peter')
<QuerySet [<Account: Peter's Account>]>
# This list contains a dictionary.
>>> Account.objects.filter(name__startswith='Peter').values()
<QuerySet [{'id': 1, 'name': 'Peter's Account'}]>
Deletion
Not many hard deletes occur in modern societies. Deletions are usually done in the form of soft deletes, such as setting a flag on an existing table which indicates that a record has been deleted.
Account.objects.filter(id=2).delete()
Models become more powerful when they relate to each other.
A many-to-one relationship maps a number of rows in one table to a single row in another table. To define a many-to-one relationship, use ForeignKey.
class ForeignKey(to, on_delete, options)
Requires two positional arguments: the instance of a class to which the model is related, and the on_delete option.
A recursive relationship (an object that has a many-to-one relationship with itself) can also be made by using models.ForeignKey('self', on_delete=models.CASCADE).
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
**question = models.ForeignKey(Question, on_delete=models.CASCADE)
The primary key of the passed instance will be used as the foreign key. Because the primary key of the Question is required by the Choice, the Question has to be saved before the Choice references so that the latter actually has a key to be referenced.
When using ForeignKey, each related class can access element sets of the other class by using the following syntax: class_lowercase.set.all()
>>> q1 = Question(question_text="What's up?", pub_date=timezone.now())
>>> q1.save()
>>> c1_1 = Choice(choice_text="Doing well", votes=0, question=q1)
>>> c1_1.save()
>>> c1_2 = Choice(choice_text="Nothing much", votes=0, question=q1)
>>> c1_2.save()
# You can also create a Choice via the Question object
>>> c1_3 = q1.choice_set.create(choice_text="Yo", votes=0)
# Add the same choice to a different choice set
>>> q2 = Question(question_text="How are you doing?", pub_date=timezone.now())
>>> q2.save()
>>> q2.choice_set.add(c1_1)
>>> c1_1.question.id
2
>>> c1_1.question
<Question: How are you doing?>
When an instance of a model that is in a relationship with an instance of another model, we need to specify what happens when one of those instances is deleted. Django provides on_delete keyword argument for this matter.
ForeignKey.on_delete
When an object referenced by a ForeignKey is deleted, Django will emulate the behavior of the SQL constraint specified by the on_delete argument.
Assuming that the ForeignKey was defined with on_delete argument set to CASCADE, related choices will be deleted if the question is deleted.
Possible values for on_delete argument (check out django.db.models)
For more information on many-to-one relationships, check Django documentation
One-to-one relationship exists when one element in the first table and one element in the second table are in an exclusive relationship.
To define a one-to-one relationship, use models.OneToOneField
Continuing with the previous example, assume that one question can only have one unique choice.
class Question(models.Model):
...
class Choice(models.Model):
...
question = models.OneToOneField(Question)
To define a many-to-many relationship, use ManyToManyField
In this case, a Question can have multiple Choice objects, and a Choice object can be in many Question objects.
This approach is convenient when the linking table only has columns for the foreign keys of each instance.
class Question(models.Model):
...
class Choice(models.Model):
...
questions = models.ManyToManyField(Question)
A table will be automatically created in the database that links the two models together, requiring no extra code from the user.
>>> q1 = Question(question_text="What is Python?")
>>> q2 = Question(question_text="What is Java?")
>>> q1.save()
>>> q2.save()
>>> c1 = Choice(choice_text="A programming language")
>>> c2 = Choice(choice_text="Awesome stuff")
>>> c1.save()
>>> c2.save()
Because the Choice model explicitly links itself to the Question model using the questions field, all of the Questions that have a Choice can be accessed. On the other hand, a Question object, which does not have an explicit link, accesses Choice objets via choice_set property.
c1.questions
q1.choice_set
When there is a need to associate data with the relationship between two models (instead of simply matching choices to questions), we can define an intermediate model with extra fields that help sepcify the relationship.
class Question(models.Model):
...
class Choice(models.Model):
...
questions = models.ManyToManyField(Question, through='Publisher')
class Publisher(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.ForeignKey(Choice, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
affiliation = models.CharField(max_length=128)
The explicit declaration in the intermediate model, Publisher, defines how the two models are related.
add(), create(), or set() methods are disabled in this kind of relationship, since they don't privde all the detail for the relationship required by the intermediate model. The only way to create new objects and update the database is to create instances through the intermediate model.
# The following methods are disabled.
>>> c1.questions.add("How is your day?")
>>> c1.questions.create(question_text="How is your day?")
>>> c1.questions.set(["How is your day?", "What is Django?"])
The remove() method is diabled for similar reasons. If the custom through table defined by the intermediate model does not enforce uniqueness on its model pairs, a remove() call won't be able to determine which instermediate model instance is to be deleted.
The clear() method can be used, however, to remove every many-to-may relationships for an instance.
>>> c1.questions.clear()
You can also access the intermediate model's information by querying the many-to-many relationship from a Question object:
>>> q1_publisher = q1.publisher_set.get(choice=c1)
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text
It is important to add __str__() methods to your models. Without this method, the objects will be represented in the interactive prompt as <Question: Question object (1)>, and not <Question: What's up?>
get_absolute_url returns a string of the url that is associated with that particular model instance. This method is useful in templates and redirect responses.
def get_absolute_url(self):
return reverse('model_name', pks={pk: self.pk})
The Meta class of a model allows the user to set properties of the class that are not and cannot be defined as fields.
class Choice(models.Model):
...
class Meta:
# Used to declare which app the model belongs to when the model is
# defined outside of an application in Installed_Apps
app_label = 'choice_app'
# Declares a name for the table instead of using the default
# app_model pattern
db_table = 'choices'
# Specifies the order of the instances returned from the database
# by field name. The default order is ascending.
# '-' at the beginning of the string means descending order
# '?' represents random ordering
ordering = ['-votes']
# Gives a singular, human-readable name for an object
# If not given, Django will use a munged version of the class name
# CamelCase will become camel case.
verbose_name = "cho"
Include the app in the user's project: add a reference to Poll app's configuration class in the INSTALLED_APPS setting in settings.py
Next, run
python manage.py makemigrations appName
to create migrations for the changes made in models.py.
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. Django will review the changes and make plans for a migration, giving each migration a number (0001, 0002, ...).
It is a better practice to include appName after the makemigrations and migrate commands, because this way ensures that migration is done in the order of reference.
Displays SQL statements for a migration.
The sqlmigrate command,
$ python manage.py sqlmigrate polls 0001
does not actually run the migration on user database; however, it prints the SQL version of migration to the screen so that the user can see what SQL Django thinks is required.
python manage.py check
similarly checks for any problems in your project without making migrations.
Migrate is the actual process of applying and unapplying changes to the database. 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. Files to be migrated are created in each app's migration class. Primary keys for each migration is automatically produced.
python manage.py migrate account 0001