Database Setup

Getting to know more about models.py

Creating models

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

  • By convention, use singular form for all class names in Django
  • To view tables in the database, type sqlite> prompt: .tables

Field Options

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])

Instance Creation

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())

Playing with the API

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 하는것보다 맞게 불러오는게 더 효율적임.

create() & save()

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.

filter() & get()

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.

Data Retrieval and Manipulations

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()

Relationships

Models become more powerful when they relate to each other.

Many-to-One Relationships

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.

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?>

Deleting Instances of Models

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)

  • CASCADE
    Django emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey.
  • PROTECT
    Prevent deletion of the referenced object by raising ProtectedError.
  • SET_NULL
    Set the ForeignKey null; only possible if null is True.
  • SET_DEFAULT
    Set the ForeignKey to its default value; a default value must be set beforehand.
  • SET()
    Set the ForeignKey to the value passed to SET().
  • DO_NOTHING

For more information on many-to-one relationships, check Django documentation

One-to-One Relationships

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.

OneToOneField

class Question(models.Model):
    ...
class Choice(models.Model):
    ...
    question = models.OneToOneField(Question)

OneToOneField documentation

Many-to-Many Relationships

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.

1. Creating a table linking two models

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)

ManyToMany documentation

Model Methods

__str__(self) methods

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(self) methods

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})

Meta Class

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"

Activating Models

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.

sqlmigrate

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

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

0개의 댓글