5. Create the Models¶
“A model is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re storing. Generally, each model maps to a single database table.”
The next step is to create the data structure with help from models.
Models define how data is saved. Usually a model represents a table in the database, and has fields, metadata and methods. With this information Django can automatically generate an interface to the database that allows object oriented access to it. This is called Object-Relation-Mapping (ORM).
In django-marcador, two models are required. You can see them in the
entity–relationship model above. Bookmark
represents the actual
bookmarks and has fields for URL, title and description. Data about who
created the bookmark and when, as well as whether the bookmark is public
or not is also stored here. This data will be used later to filter the
bookmarks. A second model Tag
represents keywords that can be
attached to bookmarks to make them easier to find. Bookmark
and
Tag
have a many-to-many relationship, so Django will create the
intermediate table automatically.
In order that Django recognises your class as a model, it must inherit
from django.db.models.Model
and be placed in models.py
in
your app directory (in this example mysite/marcador/
).
5.1. Fields¶
Fields are defined as class attributes, and map to the columns in the
table. Django has several different types of fields, in order to allow
access to the data in the most sensible way. For example CharField
means a VARCHAR
column in an SQL database. You can find a
list of field types in the Django
documentation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # encoding: utf-8
from django.contrib.auth.models import User
from django.db import models
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
class Bookmark(models.Model):
url = models.URLField()
title = models.CharField('title', max_length=255)
description = models.TextField('description', blank=True)
is_public = models.BooleanField('public', default=True)
date_created = models.DateTimeField('date created')
date_updated = models.DateTimeField('date updated')
owner = models.ForeignKey(User, verbose_name='owner',
related_name='bookmarks')
tags = models.ManyToManyField(Tag, blank=True)
|
5.2. Metadata¶
Models can contain Metadata, that
can influence how they are displayed or how they behave. They are
defined in the internal class Meta
. In the example, the display
name for presentation is set (singular and plural), as well as the
default sort order.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # encoding: utf-8
from django.contrib.auth.models import User
from django.db import models
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
class Meta:
verbose_name = 'tag'
verbose_name_plural = 'tags'
ordering = ['name']
class Bookmark(models.Model):
url = models.URLField()
title = models.CharField('title', max_length=255)
description = models.TextField('description', blank=True)
is_public = models.BooleanField('public', default=True)
date_created = models.DateTimeField('date created')
date_updated = models.DateTimeField('date updated')
owner = models.ForeignKey(User, verbose_name='owner',
related_name='bookmarks')
tags = models.ManyToManyField(Tag, blank=True)
class Meta:
verbose_name = 'bookmark'
verbose_name_plural = 'bookmarks'
ordering = ['-date_created']
|
5.3. Methods¶
You can now add Model functionality Methods for actions that apply to a single
record. For instance, it is usual to create a human readable form of the
record with the method __str__
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | # encoding: utf-8
from django.contrib.auth.models import User
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now
@python_2_unicode_compatible
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
class Meta:
verbose_name = 'tag'
verbose_name_plural = 'tags'
ordering = ['name']
def __str__(self):
return self.name
@python_2_unicode_compatible
class Bookmark(models.Model):
url = models.URLField()
title = models.CharField('title', max_length=255)
description = models.TextField('description', blank=True)
is_public = models.BooleanField('public', default=True)
date_created = models.DateTimeField('date created')
date_updated = models.DateTimeField('date updated')
owner = models.ForeignKey(User, verbose_name='owner',
related_name='bookmarks')
tags = models.ManyToManyField(Tag, blank=True)
class Meta:
verbose_name = 'bookmark'
verbose_name_plural = 'bookmarks'
ordering = ['-date_created']
def __str__(self):
return '%s (%s)' % (self.title, self.url)
def save(self, *args, **kwargs):
if not self.id:
self.date_created = now()
self.date_updated = now()
super(Bookmark, self).save(*args, **kwargs)
|
In the bookmark model, we’ll also override the method save()
to set
the right creation or last-changed date. The state of the id
field
will be used, in order to decide if the model has already been saved or
not. The field id
exists in every model - if it’s not explicitely
declared, Django will create it automatically. It is used as the primary
key of the model, to uniquely identify the record. If this field
doesn’t exist, the model hasn’t been saved yet. As a last step, the
function super()
is used to call the method save()
from the base
class (i.e. the class that we’ve inherited from).
Note
Marcador supports Python 2 and Python 3. This is achieved by the
the decorator @python_2_unicode_compatible
and by using
__str__
instead of __unicode__
as you may find it in older
versions of the documentation.
5.4. Manager¶
In order to run the database queries, there’s a manager for every model. Unless it’s otherwise defined,
the attribute objects
holds a reference to the default manager. The
queries can be altered by overriding the default manager or adding
another one.
The Django documentation explains the usage of a manager as follows:
“To retrieve objects from your database, construct a
QuerySet
via aManager
on your model class.A
QuerySet
represents a collection of objects from your database. It can have zero, one or many filters – criteria that narrow down the collection based on given parameters. In SQL terms, aQuerySet
equates to aSELECT
statement, and a filter is a limiting clause such asWHERE
orLIMIT
.”
Most of the QuerySet
methods return a new QuerySet
so you can combine
multiple of them. To access the items in a QuerySet
you can loop over it or
simply use the item’s index. For example the following Python code returns the
first item of a QuerySet
which contains all public bookmarks ordered by
their title:
>>> Bookmark.objects.filter(is_public=True).order_by('title')[0]
The SQL query executed against the database for the Python code above looks like this:
SELECT "marcador_bookmark"."id",
"marcador_bookmark"."url",
"marcador_bookmark"."title",
"marcador_bookmark"."description",
"marcador_bookmark"."is_public",
"marcador_bookmark"."date_created",
"marcador_bookmark"."date_updated",
"marcador_bookmark"."owner_id"
FROM "marcador_bookmark"
WHERE "marcador_bookmark"."is_public" = True
ORDER BY "marcador_bookmark"."title" ASC LIMIT 1
You can find a list of all QuerySet
methods in the
documentation.
In our example we’ll often show only public bookmarks so we add a
second manager to the Bookmark
model that will only
return the public bookmarks. We’ll assign it to the attribute
public
. In order to keep a reference to the default manager, we have
to explicitely assign it to the objects
attribute of the
Bookmark
class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class PublicBookmarkManager(models.Manager):
def get_queryset(self):
qs = super(PublicBookmarkManager, self).get_queryset()
return qs.filter(is_public=True)
@python_2_unicode_compatible
class Bookmark(models.Model):
url = models.URLField()
title = models.CharField('title', max_length=255)
description = models.TextField('description', blank=True)
is_public = models.BooleanField('public', default=True)
date_created = models.DateTimeField('date created')
date_updated = models.DateTimeField('date updated')
owner = models.ForeignKey(User, verbose_name='owner',
related_name='bookmarks')
tags = models.ManyToManyField(Tag, blank=True)
objects = models.Manager()
public = PublicBookmarkManager()
class Meta:
verbose_name = 'bookmark'
verbose_name_plural = 'bookmarks'
ordering = ['-date_created']
def __str__(self):
return '%s (%s)' % (self.title, self.url)
def save(self, *args, **kwargs):
if not self.id:
self.date_created = now()
self.date_updated = now()
super(Bookmark, self).save(*args, **kwargs)
|
5.5. The complete file¶
When everything is complete, the file models.py
should look as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | # encoding: utf-8
from django.contrib.auth.models import User
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now
@python_2_unicode_compatible
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
class Meta:
verbose_name = 'tag'
verbose_name_plural = 'tags'
ordering = ['name']
def __str__(self):
return self.name
class PublicBookmarkManager(models.Manager):
def get_queryset(self):
qs = super(PublicBookmarkManager, self).get_queryset()
return qs.filter(is_public=True)
@python_2_unicode_compatible
class Bookmark(models.Model):
url = models.URLField()
title = models.CharField('title', max_length=255)
description = models.TextField('description', blank=True)
is_public = models.BooleanField('public', default=True)
date_created = models.DateTimeField('date created')
date_updated = models.DateTimeField('date updated')
owner = models.ForeignKey(User, verbose_name='owner',
related_name='bookmarks')
tags = models.ManyToManyField(Tag, blank=True)
objects = models.Manager()
public = PublicBookmarkManager()
class Meta:
verbose_name = 'bookmark'
verbose_name_plural = 'bookmarks'
ordering = ['-date_created']
def __str__(self):
return '%s (%s)' % (self.title, self.url)
def save(self, *args, **kwargs):
if not self.id:
self.date_created = now()
self.date_updated = now()
super(Bookmark, self).save(*args, **kwargs)
|