5. Crear los modelos

“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.”

Models | Django documentation

El siguiente paso es crear la estructura de datos con la ayuda de modelos.

Los modelos definen cómo se guardan los datos. Normalmente, un modelo representa una tabla en una base de datos, tiene campos, meta datos y métodos. Con esta información Django puede generar automáticamente una interfaz a la base de datos que permite el acceso orientado a objetos a ella. Esto se llama mapeo objeto-relacional (MOR).

An entity–relationship model of our application

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. Campos

Los campos se definen como atributos de la clase, y se mapean a columnas en la tabla. Django tienen varios tipos diferentes de campos, para permitir acceso a los datos de la manera más razonable. Por ejemplo CharField significa una columna VARCHAR en una base de datos SQL. Puedes encontrar una lista de tipos de campos en la documentación de Django.

 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. Metadatos

Los modelos pueden contener Metadatos, que pueden influenciar cómo se muestran o cómo se comportan. Son definidos en la case interna Meta. En el ejemplo, se establece el nombre de visualización (singular y plural), así como el orden de clasificación predeterminado.

 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. Métodos

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)

En el modelo marcador, también vamos a reemplazar el método save() para establecer la fecha de creación o de último cambio correcta. El estado de el campo id sera usado para decidir si el modelo ya ha sido guardado o no. El campo id existe en todo modelo - si no es declarado explícitamente, Django lo creará automáticamente. Es usado como la llave primaria del modelo, para identificar de manera única al registro. Si este campo no existe, el modelo no ha sido guardado todavía. Como último paso, la función super() es utilizada para ejecutar el método save() de la clase base (es decir, de la clase de la que hemos heredado).

Nota

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. Director

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 a Manager 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, a QuerySet equates to a SELECT statement, and a filter is a limiting clause such as WHERE or LIMIT.”

Making queries | Django documentation

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

Cuando todo está completo, el archivo models.py debería verse así:

 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)