Разное

Django model: Модели | Документация Django 3.0

Содержание

Модели | Документация Django 3.0

Самая важная часть модели — и единственная необходимая часть модели — это список полей базы данных, которые она определяет. Поля определяются атрибутами класса. Будьте внимательны и не выбирайте имена полей, которые конфликтуют с models API, такими как clean, save или delete.

Типы полей¶

Каждое поле в вашей модели должно быть экземпляром соответствующего класса Field. Django использует типы классов полей для определения нескольких вещей:

  • Тип столбца, который сообщает базе данных, какой тип данных хранить (например, INTEGER, VARCHAR, TEXT).
  • HTML-код по умолчанию widget, используемый при визуализации поля формы (например, <input type = "text">, <select>).
  • Минимальные требования проверки, используемые в админке Django и в автоматически сгенерированных формах.

Django поставляется с десятками встроенных типов полей; Вы можете найти полный список в ссылка на поле модели. Вы можете легко написать свои собственные поля, если встроенные в Django не работают; смотрите Writing custom model fields.

Опции полей¶

Каждое поле принимает определенный набор специфичных для его типа аргументов (описано в документация по полям модели). Например, для CharField (и его подклассов) требуется аргумент max_length, который определяет размер VARCHAR поля базы данных, используемое для хранения данных.

Также есть набор общих аргументов, доступных для всех типов полей. Все необязательно. Они полностью описаны на странице опции полей модели, но вот краткий обзор наиболее часто используемых:

null
Если True, Django будет хранить пустые значения как NULL в базе данных. По умолчанию установлено значение False.

blank

Если True, поле может быть пустым. По умолчанию установлено значение False.

Обратите внимание, что это отличается от null. null связана исключительно с базой данных, тогда как blank связана с проверкой. Если поле имеет blank = True, проверка формы позволит ввести пустое значение. Если поле имеет blank = False, поле будет обязательным.

choices

Последовательность (sequence) из 2-х значных кортежей для использования в качестве выбора для этого поля. Если задан, виджет формы по умолчанию будет окном выбора вместо стандартного текстового поля и ограничит варианты выборами.

Список выбора выглядит следующим образом:

YEAR_IN_SCHOOL_CHOICES = [
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
]

Примечание

Новая миграция создается каждый раз, когда меняется порядок выборов.

Первый элемент в каждом кортеже — это значение, которое будет храниться в базе данных. Второй элемент отображается виджетом формы поля.

Для данного экземпляра модели доступ к отображаемому значению поля с choices можно получить с помощью метода get_FOO_display(). Например:

from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)

>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'

Вы также можете использовать классы перечисления, чтобы определить choices в сжатой форме:

from django.db import models

class Runner(models.Model):
    MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
    name = models.CharField(max_length=60)
    medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)

Дополнительные примеры доступны в справочнике по полям модели.

default
Значение по умолчанию для поля. Это может быть значение или вызываемый объект. Если он вызывается, он будет вызываться каждый раз, когда создается новый объект.

help_text
Дополнительный «справочный» текст для отображения с виджетом формы. Полезен для документирования, подсказки, даже если ваше поле не используется в форме.

primary_key

Если True, это поле является первичным ключом для модели.

Если вы не укажете primary_key = True для каких-либо полей в вашей модели, Django автоматически добавит IntegerField для хранения первичного ключа, поэтому вам не нужно устанавливать primary_key = True в любом из ваших полей, если только вы не хотите переопределить поведение первичного ключа по умолчанию. Для получения дополнительной информации см. Автоматические поля первичного ключа.

Поле первичного ключа доступно только для чтения. Если вы измените значение первичного ключа для существующего объекта, а затем сохраните его, новый объект будет создан вместе со старым. Например:

from django.db import models

class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)

>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
<QuerySet ['Apple', 'Pear']>

unique
Если указано True, это поле должно быть уникальным во всей таблице.

Опять же, это лишь краткие описания наиболее распространенных вариантов полей. Полную информацию можно найти в общие опции полей модели.

Автоматические поля первичного ключа¶

По умолчанию Django предоставляет каждой модели следующее поле:

id = models.AutoField(primary_key=True)

Это автоинкрементный первичный ключ.

Если вы хотите указать собственный первичный ключ, укажите primary_key = True в одном из ваших полей. Если Django увидит, что вы явно указали Field.primary_key, он не добавит автоматический столбец id.

Каждой модели требуется ровно одно поле primary_key = True (либо объявлено явно, либо добавлено автоматически).

Полные имена полей¶

Каждый тип поля, кроме ForeignKey, ManyToManyField и OneToOneField, принимает необязательный первый позиционный аргумент — подробное имя. Если подробное имя не указано, Django автоматически создаст его, используя имя атрибута поля, преобразовав подчеркивание в пробелы.

В этом примере подробное имя — это "person's first name":

first_name = models.CharField("person's first name", max_length=30)

В этом примере подробное имя — «first name»`:

first_name = models.CharField(max_length=30)

ForeignKey, ManyToManyField и OneToOneField требуют, чтобы первый аргумент был классом модели , поэтому используйте аргумент verbose_name для указания подробного имени:

poll = models.ForeignKey(
    Poll,
    on_delete=models.CASCADE,
    verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    verbose_name="related place",
)

Соглашение: verbose_name не должно начинаться с заглавной буквы первой буквы. Django автоматически использует заглавную букву, где это необходимо.

Отношения¶

Очевидно, что сила реляционных баз данных заключается в том, что таблицы связаны друг с другом. Django предлагает способы определения трех наиболее распространенных типов отношений с базой данных: многие-к-одному (many-to-one), многие-ко-многим (many-to-many) и один-к-одному (one-to-one).

Отношения много-к-одному¶

Чтобы определить отношение многие-к-одному, используйте django.db.models.ForeignKey, так же, как и любой другой тип Field: включив его в качестве атрибута класса вашей модели.

ForeignKey требует позиционный аргумент: класс, к которому относится модель.

Например, если у модели Car есть Manufacturer — то есть, Manufacturer делает несколько автомобилей, но у каждого Car есть только один Manufacturer — используйте следующие определения:

from django.db import models

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

Вы также можете создать рекурсивные отношения (объект, имеющий отношение «многие к одному») и отношения с моделями, которые еще не определены; смотрите ссылку на поле модели для подробностей.

Предполагается, но не обязательно, что имя поля ForeignKey (factory в приведенном выше примере) будет именем модели в нижнем регистре. Вы можете, конечно, назвать поле как хотите. Например:

class Car(models.Model):
    company_that_makes_it = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
    )
    # ...
Отношения многие ко многим¶

Чтобы определить отношение «многие ко многим», используйте ManyToManyField. Вы используете его так же, как и любой другой тип Field: включив его в качестве атрибута класса вашей модели.

ManyToManyField требует позиционный аргумент: класс, к которому относится модель.

Например, если Pizza имеет несколько объектов Topping — то есть Topping может быть на нескольких пиццах, а каждый Pizza имеет несколько начинок — вот как вы должны это представить в модели:

from django.db import models

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

Как и в случае ForeignKey, вы также можете создать рекурсивные отношения (объект, имеющий отношение «многие ко многим» к себе) и когда отношения с моделями еще не определены.

Предполагается, но не обязательно, чтобы имя ManyToManyField (toppings в примере выше) было множественным числом, описывающим набор связанных объектов модели.

Неважно, какая модель имеет ManyToManyField, но вы должны поместить ее только в одну из моделей, а не в обе.

Как правило, экземпляры ManyToManyField должны помещаться в объект, который будет редактироваться в форме. В приведенном выше примере toppings находится в Pizza (а не в Topping с pizzas ManyToManyField), потому что это более естественно думать о пицце, имеющей начинки, чем начинки на нескольких пиццах. Как указано выше, форма Pizza позволит пользователям выбирать начинки.

Поля ManyToManyField также принимают ряд дополнительных аргументов, которые объясняются в по ссылке. Эти параметры помогают определить, как должны работать отношения; все необязательно.

Дополнительные поля в отношениях «многие ко многи컶

Когда вы имеете дело только с отношениями «многие ко многим», такими как смешивание и сопоставление пиццы и начинки, вам нужен стандартный ManyToManyField. Однако иногда вам может потребоваться связать данные с отношениями между двумя моделями.

Например, рассмотрим случай, когда приложение отслеживает музыкальные группы, к которым принадлежат музыканты. Существует отношение «многие ко многим» между человеком и группами, членами которых он является, поэтому вы можете использовать ManyToManyField для представления этих отношений. Тем не менее, есть много деталей о членстве, которые вы, возможно, захотите получить, например, о дате, когда человек присоединился к группе.

В этих ситуациях Django позволяет вам указать модель, которая будет использоваться для управления отношением «многие ко многим». Затем вы можете поместить дополнительные поля в промежуточную модель. Промежуточная модель связана с ManyToManyField с помощью аргумента через, указывающего на модель, которая будет выступать в качестве посредника. Для нашего примера с музыкантами код будет выглядеть примерно так:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

При настройке модели-посредника вы явно указываете внешние ключи для моделей, которые участвуют в отношениях «многие ко многим». Это явное объявление определяет, как связаны две модели.

Есть несколько ограничений на промежуточную модель:

  • Ваша промежуточная модель должна содержать один — и только один — внешний ключ к исходной модели (в нашем примере это будет Group), или вы должны явно указать внешние ключи, которые Django должен использовать для отношений, используя ManyToManyField.through_fields. Если у вас есть более одного внешнего ключа и through_fields не указан, возникнет ошибка при проверке. Аналогичное ограничение применяется к внешнему ключу для целевой модели (в нашем примере это Person).
  • Для модели, имеющей отношение «многие ко многим» к себе через посредническую модель, допускается два внешних ключа к одной и той же модели, но они будут рассматриваться как две (разные) стороны отношения «многие ко многим». Если существует больше, чем два внешних ключа, вы также должны указать « through_fields«, как указано выше, иначе будет возникать ошибка проверки.

Теперь, когда вы настроили свой класс ManyToManyField для использования своей модели-посредника (в данном случае Membership), вы готовы начать создавать отношения многие ко многим. Сделать это можно путем создания экземпляров промежуточной модели:

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>

Вы также можете использовать add(), create() или set() для создания отношений, если вы укажете through_defaults для любых обязательных полей:

>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})

Вы можете предпочесть создание экземпляров промежуточной модели напрямую.

Если пользовательская сквозная таблица, определенная промежуточной моделью, не обеспечивает уникальность в паре (model1, model2), допуская несколько значений, то вызов remove() удалит все промежуточные экземпляры модели:

>>> Membership.objects.create(person=ringo, group=beatles,
...     date_joined=date(1968, 9, 4),
...     invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>

Метод clear() можно использовать для удаления всех отношений «многие ко многим» для экземпляра:

>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>

После того как вы установили отношения «многие ко многим», вы можете создавать запросы. Как и в случае с обычными отношениями «многие ко многим», вы можете выполнять запросы, используя атрибуты модели «многие ко многим»:

# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>

Поскольку вы используете промежуточную модель, вы также можете запросить ее атрибуты:

# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
...     group__name='The Beatles',
...     membership__date_joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>

Если вам нужен доступ к информации о членстве, вы можете сделать это, напрямую запросив модель Membership:

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

Другой способ получить доступ к той же информации — запросить обратные отношения «многие ко многим» из объекта Person:

>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
Отношения один-к-одному¶

Чтобы определить отношение один-к-одному, используйте OneToOneField. Используйте его, как и любой другой тип Field: включив его в качестве атрибута класса вашей модели.

Это отношение наиболее полезно для первичного ключа объекта, когда этот объект каким-либо образом «расширяет» другой объект.

OneToOneField требует позиционный аргумент: класс, к которому относится модель.

Например, если бы вы строили базу данных «мест», вы бы в нее встроили довольно стандартные вещи, такие как адрес, номер телефона и т.п. Затем, если вы хотите создать базу данных ресторанов поверх мест, вместо того, чтобы повторять себя и копировать эти поля в модели Restaurant, вы можете сделать так, чтобы Restaurant имел OneToOneField указывающий на Place (потому что ресторан «является» местом; на самом деле, чтобы справиться с этим, вы могли использовать наследование, которое включает в себя неявное отношение один к одному).

Как и в случае ForeignKey, можно определить рекурсивные отношения и ссылки на еще не определенные модели.

Поля OneToOneField также принимают необязательный аргумент parent_link.

Классы OneToOneField автоматически становились первичным ключом модели. Это больше не так (хотя вы можете вручную передать аргумент primary_key, если хотите). Таким образом, теперь возможно иметь несколько полей типа OneToOneField в одной модели.

Справочник по методам модели | Документация Django 3.1

Этот документ описывает детали API Model. Он основан на материале, представленном в руководствах model и запрос к базе данных, так что вы, вероятно, захотите прочитать и понять эти документы, прежде чем читать этот.

В этом справочнике мы будем использовать пример моделей Weblog’а, представленный в руководстве по запросам к базе данных.

Создание объектов¶

Чтобы создать новый экземпляр модели, создайте его экземпляр, как и любой другой класс Python:


class Model(**kwargs)[исходный код]¶

Ключевыми аргументами являются просто имена полей, которые вы определили в вашей модели. Обратите внимание, что создание экземпляра модели никоим образом не затрагивает вашу базу данных; для этого вам необходимо выполнить save().

Примечание

Вы можете испытать желание изменить модель, переопределив метод __init__. Однако, если вы сделаете это, позаботьтесь о том, чтобы не изменять сигнатуру вызывающего, так как любое изменение может помешать сохранению экземпляра модели. Вместо того, чтобы переопределять __init__, попробуйте использовать один из следующих подходов:

  1. Добавьте метод в класс модели:

    from django.db import models
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        @classmethod
        def create(cls, title):
            book = cls(title=title)
            # do something with the book
            return book
    
    book = Book.create("Pride and Prejudice")
    

  2. Добавить метод в пользовательский менеджер (обычно предпочтительнее):

    class BookManager(models.Manager):
        def create_book(self, title):
            book = self.create(title=title)
            # do something with the book
            return book
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        objects = BookManager()
    
    book = Book.objects.create_book("Pride and Prejudice")
    

Настройка загрузки модели¶


classmethod Model.from_db(db, field_names, values)[исходный код]¶

Метод from_db() можно использовать для настройки создания экземпляра модели при загрузке из базы данных.

Аргумент db содержит псевдоним базы данных для базы данных, из которой загружена модель, field_names содержит имена всех загруженных полей, а values содержит загруженные значения для каждого поля в field_names. field_names находятся в том же порядке, что и values. Если присутствуют все поля модели, то values гарантированно будут в том порядке, в котором их ожидает __init__(). То есть экземпляр может быть создан с помощью cls(* values). Если какие-либо поля отложены, они не появятся в field_names. В этом случае присвойте значение django.db.models.DEFERRED каждому из пропущенных полей.

In addition to creating the new model, the from_db() method must set the
adding and db flags in the new instance’s _state attribute.

Ниже приведен пример, показывающий, как записать начальные значения полей, загружаемых из базы данных:

from django.db.models import DEFERRED

@classmethod
def from_db(cls, db, field_names, values):
    # Default implementation of from_db() (subject to change and could
    # be replaced with super()).
    if len(values) != len(cls._meta.concrete_fields):
        values = list(values)
        values.reverse()
        values = [
            values.pop() if f.attname in field_names else DEFERRED
            for f in cls._meta.concrete_fields
        ]
    instance = cls(*values)
    instance._state.adding = False
    instance._state.db = db
    # customization to store the original field values on the instance
    instance._loaded_values = dict(zip(field_names, values))
    return instance

def save(self, *args, **kwargs):
    # Check how the current values differ from ._loaded_values. For example,
    # prevent changing the creator_id of the model. (This example doesn't
    # support cases where 'creator_id' is deferred).
    if not self._state.adding and (
            self.creator_id != self._loaded_values['creator_id']):
        raise ValueError("Updating the value of creator isn't allowed")
    super().save(*args, **kwargs)

В приведенном выше примере показана полная реализация from_db(), чтобы прояснить, как это делается. В этом случае, конечно, можно было бы просто использовать вызов super() в методе from_db().

Обновление объектов из базы данных¶

Если вы удаляете поле из экземпляра модели, при повторном доступе к нему значение из базы данных снова загружается:

>>> obj = MyModel.objects.first()
>>> del obj.field
>>> obj.field  # Loads the field from the database

Model.refresh_from_db(using=None, fields=None)[исходный код]¶

Если вам нужно перезагрузить значения модели из базы данных, вы можете использовать метод refresh_from_db(). Когда этот метод вызывается без аргументов, выполняется следующее:

  1. Все неотложенные поля модели обновляются до значений, присутствующих в настоящее время в базе данных.
  2. Все кэшированные отношения очищаются перезагрузкой экземпляра.

Только поля модели загружаются из базы данных. Другие зависящие от базы данных значения, такие как аннотации, не перезагружаются. Любые атрибуты @cached_property также не очищаются.

Перезагрузка происходит из базы данных, из которой был загружен экземпляр, или из базы данных по умолчанию, если экземпляр не был загружен из базы данных. Аргумент using может использоваться для принудительной перезагрузки базы данных.

Можно принудительно загрузить набор полей, используя аргумент fields.

Например, чтобы проверить, что вызов update() привел к ожидаемому обновлению, вы можете написать тест, подобный следующему:

def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F('val') + 1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object's updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db()
    self.assertEqual(obj.val, 2)

Обратите внимание, что при доступе к отложенным полям загрузка значения отложенного поля происходит с помощью этого метода. Таким образом, можно настроить способ отложенной загрузки. В приведенном ниже примере показано, как можно перезагрузить все поля экземпляра при перезагрузке отложенного поля:

class ExampleModel(models.Model):
    def refresh_from_db(self, using=None, fields=None, **kwargs):
        # fields contains the name of the deferred field to be
        # loaded.
        if fields is not None:
            fields = set(fields)
            deferred_fields = self.get_deferred_fields()
            # If any deferred field is going to be loaded
            if fields.intersection(deferred_fields):
                # then load all of them
                fields = fields.union(deferred_fields)
        super().refresh_from_db(using, fields, **kwargs)

Model.get_deferred_fields()[исходный код]¶

Вспомогательный метод, который возвращает набор, содержащий имена атрибутов всех тех полей, которые в данный момент откладываются для этой модели.

Проверка объектов¶

Существует три этапа проверки модели:

  1. Проверка полей модели — Model.clean_fields()
  2. Проверка модели полностью — Model.clean()
  3. Проверка уникальности полей — Model.validate_unique()

Все три шага выполняются при вызове метода модели full_clean().

Когда вы используете ModelForm, вызов is_valid() выполнит эти шаги проверки для всех полей, включенных в форму. Смотрите документацию ModelForm для получения дополнительной информации. Вам нужно только вызывать метод модели full_clean(), если вы планируете самостоятельно обрабатывать ошибки проверки или если вы исключили поля из ModelForm, для которых требуется проверка.


Model.full_clean(exclude=None, validate_unique=True)[исходный код]¶

Этот метод вызывает Model.clean_fields(), Model.clean() и Model.validate_unique() (если validate_unique равно True), в этом порядке и вызывает ValidationError, который имеет атрибут message_dict, содержащий ошибки всех трех этапов.

Необязательный аргумент exclude может использоваться для предоставления списка имен полей, которые можно исключить из проверки и очистки. ModelForm использует этот аргумент, чтобы исключить проверку полей, которые отсутствуют в вашей форме, поскольку любые возникшие ошибки не могут быть исправлены пользователем.

Обратите внимание, что full_clean() не будет автоматически вызываться при вызове метода модели save(). Вам нужно будет вызвать его вручную, если вы хотите запустить одноэтапную проверку моделей для ваших собственных моделей, созданных вручную. Например:

from django.core.exceptions import ValidationError
try:
    article.full_clean()
except ValidationError as e:
    # Do something based on the errors contained in e.message_dict.
    # Display them to a user, or handle them programmatically.
    pass

Первый шаг, который выполняет full_clean(), — это очистка каждого отдельного поля.


Model.clean_fields(exclude=None)[исходный код]¶

Этот метод проверит все поля в вашей модели. Необязательный аргумент exclude позволяет вам предоставить список имен полей, которые нужно исключить из проверки. Он вызовет ValidationError, если какие-либо поля не пройдут проверку.

Второй шаг, который выполняет full_clean(), заключается в вызове Model.clean(). Этот метод должен быть переопределен для выполнения пользовательской проверки вашей модели.


Model.clean()[исходный код]¶

Этот метод должен использоваться для обеспечения пользовательской проверки модели и для изменения атрибутов вашей модели, если это необходимо. Например, вы можете использовать его для автоматического предоставления значения для поля или для проверки, которая требует доступа к более чем одному полю:

import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError(_('Draft entries may not have a publication date.'))
        # Set the pub_date for published items if it hasn't been set already.
        if self.status == 'published' and self.pub_date is None:
            self.pub_date = datetime.date.today()

Однако обратите внимание, что как Model.full_clean(), метод clean() модели не вызывается, когда вы вызываете метод вашей модели save().

В приведенном выше примере исключение ValidationError, вызванное Model.clean(), было создано со строкой, поэтому оно будет сохранено в специальном ключе словаря ошибок NON_FIELD_ERRORS. Этот ключ используется для ошибок, которые связаны со всей моделью, а не с конкретным полем:

from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
try:
    article.full_clean()
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

Чтобы назначить исключения конкретному полю, создайте экземпляр ValidationError со словарем, где ключами являются имена полей. Мы могли бы обновить предыдущий пример, чтобы присвоить ошибку полю pub_date:

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError({'pub_date': _('Draft entries may not have a publication date.')})
        ...

Если вы обнаружите ошибки в нескольких полях во время Model.clean(), вы также можете передать имена полей отображения словаря в ошибки:

raise ValidationError({
    'title': ValidationError(_('Missing title.'), code='required'),
    'pub_date': ValidationError(_('Invalid date.'), code='invalid'),
})

Наконец, full_clean() проверит любые уникальные ограничения на вашей модели.

Как вызвать специфичные для поля ошибки проверки, если эти поля не отображаются в ModelForm

Вы не можете выдавать ошибки проверки в Model.clean() для полей, которые не отображаются в модельной форме (форма может ограничивать свои поля, используя Meta.fields или Meta.exclude). Это вызовет ValueError, потому что ошибка проверки не сможет быть связана с исключенным полем.

Чтобы обойти эту дилемму, вместо этого переопределите Model.clean_fields(), так как он получает список полей, которые исключены из проверки. Например:

class Article(models.Model):
    ...
    def clean_fields(self, exclude=None):
        super().clean_fields(exclude=exclude)
        if self.status == 'draft' and self.pub_date is not None:
            if exclude and 'status' in exclude:
                raise ValidationError(
                    _('Draft entries may not have a publication date.')
                )
            else:
                raise ValidationError({
                    'status': _(
                        'Set status to draft if there is not a '
                        'publication date.'
                     ),
                })

Model.validate_unique(exclude=None)[исходный код]¶

Этот метод похож на clean_fields(), но проверяет все ограничения уникальности вашей модели вместо отдельных значений полей. Необязательный аргумент exclude позволяет вам предоставить список имен полей, исключаемых из проверки. Он вызовет ValidationError, если какие-либо поля не пройдут проверку.

Обратите внимание, что если вы предоставите аргумент exclude для validate_unique(), любое ограничение unique_together, включающее одно из предоставленных вами полей, проверяться не будет.

Сохранение объектов¶

Чтобы сохранить объект обратно в базу данных, вызовите save():


Model.save(force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)[исходный код]¶

Если вы хотите настроить сохранение, вы можете переопределить этот метод save(). Смотрите Переопределение методов модели для более подробной информации.

Процесс сохранения модели также имеет некоторые тонкости; см. разделы ниже.

Автоинкрементные первичные ключи¶

Если модель имеет AutoField — автоинкрементный первичный ключ — тогда это автоинкрементное значение будет вычислено и сохранено как атрибут вашего объекта при первом вызове save():

>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id     # Returns None, because b2 doesn't have an ID yet.
>>> b2.save()
>>> b2.id     # Returns the ID of your new object.

Невозможно определить, какое значение идентификатора будет перед вызовом save(), потому что это значение рассчитывается вашей базой данных, а не Django.

Для удобства каждая модель имеет AutoField с именем id по умолчанию, если вы явно не укажете primary_key=True для поля в вашей модели. Смотрите документацию для AutoField для получения более подробной информации.

Свойство pk

Model.pk

Независимо от того, определяете ли вы поле первичного ключа самостоятельно или позволяете Django предоставить его для вас, каждая модель будет иметь свойство, называемое pk. Он ведет себя как обычный атрибут в модели, но на самом деле является псевдонимом для любого атрибута, являющегося полем первичного ключа для модели. Вы можете прочитать и установить это значение, как и для любого другого атрибута, и он обновит правильное поле в модели.

Явное указание значений авто-первичного ключа¶

Если модель имеет AutoField, но вы хотите явно определить идентификатор нового объекта при сохранении, просто определите его явно перед сохранением, а не полагайтесь на автоматическое назначение идентификатора:

>>> b3 = Blog(id=3, name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b3.id     # Returns 3.
>>> b3.save()
>>> b3.id     # Returns 3.

Если вы назначаете значения авто-первичного ключа вручную, убедитесь, что вы не используете уже существующее значение первичного ключа! Если вы создаете новый объект с явным значением первичного ключа, который уже существует в базе данных, Django будет предполагать, что вы изменяете существующую запись, а не создаете новую.

Учитывая приведенный выше пример блога 'Cheddar Talk', этот пример переопределяет предыдущую запись в базе данных:

b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.')
b4.save()  # Overrides the previous blog with ID=3!

Посмотрите, «Как Django определяет UPDATE или INSERT» ниже, по той причине, по которой это происходит.

Явное указание значений авто-первичного ключа в основном полезно для массового сохранения объектов, когда вы уверены, что у вас не будет коллизии первичного ключа.

Если вы используете PostgreSQL, возможно, потребуется обновить последовательность, связанную с первичным ключом; смотрите Manually-specifying values of auto-incrementing primary keys.

Что происходит, когда вы сохраняете?¶

Когда вы сохраняете объект, Django выполняет следующие шаги:

  1. Посылает сигнал предварительного сохранения pre-save. Передается сигнал pre_save, позволяющий функциям, прослушивающим этот сигнал, что-то делать.

  2. Предварительная обработка данных. Каждый метод поля pre_save() вызывается для выполнения любой необходимой автоматической модификации данных. Например, поля даты/времени переопределяют pre_save() для реализации auto_now_add и auto_now.

  3. Подготовка данных для базы данных. Метод каждого поля get_db_prep_save() должен предоставить свое текущее значение в виде данных, которые можно записать в базу данных.

    Большинство полей не требуют подготовки данных. Простые типы данных, такие как целые числа и строки, «готовы к записи» в виде объекта Python. Однако более сложные типы данных часто требуют некоторой модификации.

    Например, поля DateField используют объект datetime Python для хранения данных. Базы данных не хранят объекты datetime, поэтому значение поля должно быть преобразовано в ISO-совместимую строку даты для вставки в базу данных.

  4. Вставка данных в базу данных. Предварительно обработанные, подготовленные данные составляются в оператор SQL для вставки в базу данных.

  5. Посылает сигнал post-save после сохранения. Передается сигнал post_save, позволяющий любым функциям, прослушивающим этот сигнал, что-то делать.

Как Django узнает, когда использовать UPDATE или INSERT¶

Возможно, вы заметили, что объекты базы данных Django используют один и тот же метод save() для создания и изменения объектов. Django абстрагируется от необходимости использовать операторы INSERT или UPDATE. В частности, когда вы вызываете save() и атрибут первичного ключа объекта не определяет default, Django следует этому алгоритму:

  • Если для атрибута первичного ключа объекта установлено значение, которое оценивается как True (т.е. значение, отличное от None или пустой строки), Django выполняет UPDATE.
  • Если атрибут первичного ключа объекта не установлен или если UPDATE ничего не обновил (например, если первичному ключу присвоено значение, которого нет в базе данных), Django выполняет INSERT.

Если атрибут первичного ключа объекта определяет default, тогда Django выполняет UPDATE, если это существующий экземпляр модели и первичный ключ имеет значение, которое существует в базе данных. В противном случае Django выполняет INSERT.

Единственное, что здесь нужно сделать, это то, что вы должны быть осторожны, чтобы не указывать значение первичного ключа явно при сохранении новых объектов, если вы не можете гарантировать, что значение первичного ключа не используется. Подробнее об этом нюансе смотрите в разделе Explicitly specifying auto-primary-key values и Форсирование INSERT или UPDATE ниже.

В Django 1.5 и более ранних версиях Django сделал SELECT, когда был установлен атрибут первичного ключа. Если SELECT нашел строку, то Django сделал UPDATE, в противном случае он сделал INSERT. Старый алгоритм приводит к еще одному запросу в случае UPDATE. В некоторых редких случаях база данных не сообщает, что строка была обновлена, даже если база данных содержит строку для значения первичного ключа объекта. Примером является триггер PostgreSQL ON UPDATE, который возвращает NULL. В таких случаях можно вернуться к старому алгоритму, установив параметр select_on_save в True.

Changed in Django 3.0:

Model.save() больше не пытается найти строку при сохранении нового экземпляра Model и предоставляется значение по умолчанию для первичного ключа, и всегда выполняет INSERT.

INSERT или UPDATE¶

В некоторых редких случаях необходимо иметь возможность заставить метод save() выполнять SQL INSERT и не отступать от выполнения UPDATE. Или наоборот: обновите, если это возможно, но не вставляйте новую строку. В этих случаях вы можете передать параметры force_insert=True или force_update=True методу save(). Очевидно, что передача обоих параметров является ошибкой: вы не можете одновременно вставлять и обновлять!

Это должно быть очень редко, что вам нужно будет использовать эти параметры. Django почти всегда делает правильные вещи, и попытка переопределить это приведет к ошибкам, которые трудно отследить. Эта функция предназначена только для расширенного использования.

Использование update_fields приведет к обновлению аналогично force_update.

Обновление атрибутов на основе существующих полей¶

Иногда вам нужно выполнить простую арифметическую задачу в поле, например, увеличить или уменьшить текущее значение. Одним из способов достижения этого является выполнение арифметики в Python, например:

>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold += 1
>>> product.save()

Если старое значение number_sold, полученное из базы данных, равнялось 10, то значение 11 будет записано обратно в базу данных.

Процесс можно сделать устойчивым избегание условия гонки, а также немного быстрее, выражая обновление относительно исходного значения поля, а не как явное присвоение новой стоимости. Django предоставляет F выражение для выполнения такого рода относительного обновления. Используя F выражения, предыдущий пример выражается как:

>>> from django.db.models import F
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.save()

Для получения дополнительной информации смотрите документацию по F выражениям и их использование в запросах на обновление.

Указание полей для сохранения¶

Если в save() передается список имен полей в аргументе update_fields, будут обновлены только поля, названные в этом списке. Это может быть желательно, если вы хотите обновить только одно или несколько полей объекта. Отказ от обновления всех полей модели в базе данных приведет к небольшому выигрышу в производительности. Например:

product.name = 'Name changed again'
product.save(update_fields=['name'])

Аргумент update_fields может быть любым итеративным, содержащим строки. Пустая итерация update_fields пропустит сохранение. Значение None выполнит обновление для всех полей.

Указание update_fields приведет к обновлению.

При сохранении модели, полученной с помощью отложенной загрузки модели (only() или defer()) обновляются только поля, загруженные из БД. По сути, в этом случае происходит автоматическое update_fields. Если вы назначите или измените какое-либо значение отложенного поля, поле будет добавлено в обновленные поля.

Удаление объектов¶


Model.delete(using=DEFAULT_DB_ALIAS, keep_parents=False)[исходный код]¶

Выдает SQL DELETE для объекта. Это только удаляет объект в базе данных; Экземпляр Python все еще будет существовать и все еще будет содержать данные в своих полях. Этот метод возвращает количество удаленных объектов и словарь с количеством удалений на тип объекта.

Для получения более подробной информации, в том числе о том, как массово удалять объекты, смотрите themes-db-query-delete.

Если вам нужно индивидуальное поведение удаления, вы можете переопределить метод delete(). Смотрите Переопределение методов модели для более подробной информации.

Иногда с помощью наследование от нескольких таблиц вы можете удалить только данные дочерней модели. Указание keep_parents=True сохранит данные родительской модели.

Упаковывание объектов¶

Когда вы pickle модель, ее текущее состояние сохраняется. Когда вы откроете его, он будет содержать экземпляр модели в тот момент, когда он был выбран, а не данные, которые в данный момент находятся в базе данных.

Вы не можете использовать pickle между версиями

Упакованные модели действительны только для той версии Django, которая использовалась для их генерации. Если вы генерируете pickle с использованием Django версии N, нет никакой гарантии, что pickle будет читаться с Django версии N+1. pickle не должны использоваться как часть долгосрочной архивной стратегии.

Так как ошибки совместимости с pickle могут быть трудно диагностируемыми, например, незаметно поврежденные объекты, возникает «RuntimeWarning», когда вы пытаетесь распаковать модель в версии Django, отличной от той, в которой она была упакована.

Другие методы экземпляра модели¶

Несколько методов объекта имеют специальные цели.

__str__()


Model.__str__()[исходный код]¶

Метод __str__() вызывается всякий раз, когда вы вызываете str() для объекта. Django использует str(obj) в нескольких местах. В частности, для отображения объекта на сайте администратора Django и в качестве значения, вставляемого в шаблон при отображении объекта. Таким образом, вы всегда должны возвращать хорошее, удобочитаемое представление модели из метода __str__().

Например:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def __str__(self):
        return '%s %s' % (self.first_name, self.last_name)

__eq__()


Model.__eq__()[исходный код]¶

Метод равенства определен так, что экземпляры с одинаковым значением первичного ключа и одним и тем же конкретным классом считаются равными, за исключением того, что экземпляры со значением первичного ключа None не равны ничему, кроме самих себя. Для прокси-моделей конкретный класс определяется как первый непрокси-родитель модели; для всех остальных моделей это просто класс модели.

Например:

from django.db import models

class MyModel(models.Model):
    id = models.AutoField(primary_key=True)

class MyProxyModel(MyModel):
    class Meta:
        proxy = True

class MultitableInherited(MyModel):
    pass

# Primary keys compared
MyModel(id=1) == MyModel(id=1)
MyModel(id=1) != MyModel(id=2)
# Primary keys are None
MyModel(id=None) != MyModel(id=None)
# Same instance
instance = MyModel(id=None)
instance == instance
# Proxy model
MyModel(id=1) == MyProxyModel(id=1)
# Multi-table inheritance
MyModel(id=1) != MultitableInherited(id=1)

__hash__()


Model.__hash__()[исходный код]¶

Метод __hash__() основан на значении первичного ключа экземпляра. Это эффективно hash(obj.pk). Если у экземпляра нет значения первичного ключа, то будет вызвано TypeError (в противном случае метод __hash__() будет возвращать разные значения до и после сохранения экземпляра, но изменяя __hash__() значение экземпляра запрещено в Python.

get_absolute_url()


Model.get_absolute_url()¶

Определите метод get_absolute_url(), чтобы сообщить Django, как рассчитать канонический URL для объекта. По мнению вызывающих, этот метод должен возвращать строку, которая может использоваться для ссылки на объект по HTTP.

Например:

def get_absolute_url(self):
    return "/people/%i/" % self.id

Хотя этот код является правильным и простым, он может быть не самым переносимым способом написания такого рода метода. Функция reverse() обычно является наилучшим подходом.

Например:

def get_absolute_url(self):
    from django.urls import reverse
    return reverse('people.views.details', args=[str(self.id)])

В одном месте Django использует get_absolute_url() в приложении администратора. Если объект определяет этот метод, страница редактирования объекта будет иметь ссылку «View on site», которая приведет вас непосредственно к общему представлению объекта, как указано в get_absolute_url().

Аналогично, пара других частей Django, таких как инфраструктура канала синдикации, используют get_absolute_url(), когда она определена. Если для каждого экземпляра вашей модели имеет смысл иметь уникальный URL-адрес, вы должны определить get_absolute_url().

Предупреждение

Вы должны избегать создания URL из неподтвержденного пользовательского ввода, чтобы уменьшить возможности ссылки или перенаправления:

def get_absolute_url(self):
    return '/%s/' % self.name

Если self.name равно '/example.com', это возвращает '//example.com/', который, в свою очередь, является действительным URL-адресом схемы, но не ожидаемым '/%2Fexample.com/'.

Хорошей практикой является использование get_absolute_url() в шаблонах вместо жесткого кодирования URL-адресов ваших объектов. Например, этот код шаблона плох:

<!-- BAD template code. Avoid! -->
<a href="/people/{{ object.id }}/">{{ object.name }}</a>

Этот шаблон кода намного лучше:

<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>

Логика здесь заключается в том, что если вы изменяете структуру URL-адресов ваших объектов, даже для чего-то простого, например, для исправления орфографической ошибки, вам не нужно отслеживать каждое место, где может быть создан URL-адрес. Укажите его один раз в get_absolute_url() и пусть весь ваш другой код вызывает только его.

Примечание

Строка, которую вы возвращаете из get_absolute_url() должна содержать только символы ASCII (требуется спецификацией URI RFC 2396#section-2) и быть при необходимости закодированной в URL.

Код и шаблоны, вызывающие get_absolute_url(), должны иметь возможность использовать результат напрямую без какой-либо дальнейшей обработки. Вы можете использовать функцию django.utils.encoding.iri_to_uri(), чтобы помочь с этим, если вы используете строки, содержащие символы вне диапазона ASCII.

Дополнительные методы экземпляра¶

В дополнение к save(), delete(), у объекта модели могут быть некоторые из следующих методов:


Model.get_FOO_display()¶

Для каждого поля, для которого установлено choices, объект будет иметь метод get_FOO_display(), где FOO — это имя поля. Этот метод возвращает «удобочитаемое» значение поля.

Например:

from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'

Model.get_next_by_FOO(**kwargs


Model.get_previous_by_FOO(**kwargs

Для каждого DateField и DateTimeField, которые не имеют null=True, объект будет иметь методы get_next_by_FOO() и get_previous_by_FOO(), где FOO — это имя поля. Это возвращает следующий и предыдущий объект относительно поля даты, вызывая исключение DoesNotExist, когда это необходимо.

Оба эти метода будут выполнять свои запросы с использованием менеджера по умолчанию для модели. Если вам нужно эмулировать фильтрацию, используемую настраиваемым менеджером, или вы хотите выполнить одноразовую настраиваемую фильтрацию, оба метода также принимают необязательные ключевые аргументы, которые должны иметь формат, описанный в Поиск по полям.

Обратите внимание, что в случае идентичных значений даты эти методы будут использовать первичный ключ в качестве прерывателя связей. Это гарантирует, что никакие записи не будут пропущены или дублированы. Это также означает, что вы не можете использовать эти методы на несохраненных объектах.

Переопределение дополнительных методов экземпляра

В большинстве случаев переопределение или наследование get_FOO_display(), get_next_by_FOO() и get_previous_by_FOO() должны работать как положено. Однако поскольку они добавляются метаклассом, нецелесообразно учитывать все возможные структуры наследования. В более сложных случаях вы должны переопределить Field.contribute_to_class() для установки необходимых вам методов.

Другие атрибуты¶

_state


Model._state

The _state attribute refers to a ModelState object that tracks
the lifecycle of the model instance.

The ModelState object has two attributes: adding, a flag which is
True if the model has not been saved to the database yet, and db,
a string referring to the database alias the instance was loaded from or
saved to.

Newly instantiated instances have adding=True and db=None,
since they are yet to be saved. Instances fetched from a QuerySet
will have adding=False and db set to the alias of the associated
database.

Объект модели — Документация Django 1.8

Этот раздел описывает Model API. Изложенный материал опирается на материал, изложенный в разделах о моделях и выполнении запросов, возможно вам следует прочитать их перед прочтением этого раздела.

В примерах будут использованы :ref:` примеры моделей web-блога <queryset-model-example>` представленные в разделе о выполнении запросов.

Создание объектов

Чтобы создать объект модели, просто создайте ее экземпляр как любого другого класса Python:

class Model(**kwargs)

Именованные аргументы – это названия полей определенных в модели. Создание экземпляра модели не выполняет никаких запросов к базе данных; для сохранения вызовите метод save().

Примечание

Возможно, вам захочется переопределить метод __init__. В таком случае не переопределяйте сигнатуру вызова этого метода, иначе объект модели может не сохраняться. Вместо переопределения __init__ лучше используйте один из следующих подходов:

  1. Добавить метод класса в модель:

    from django.db import models
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        @classmethod
        def create(cls, title):
            book = cls(title=title)
            # do something with the book
            return book
    
    book = Book.create("Pride and Prejudice")
    
  2. Добавить метод в менеджер модели(лучший вариант):

    class BookManager(models.Manager):
        def create_book(self, title):
            book = self.create(title=title)
            # do something with the book
            return book
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        objects = BookManager()
    
    book = Book.objects.create_book("Pride and Prejudice")
    

Настройка загрузки модели

classmethod Model.from_db(db, field_names, values)

Добавлено в Django 1.8.

Метод from_db() позволяет настроить создания экземпляра модели при загрузке данных из базы данных.

Аргумент db содержит название базы данных, из которой загружается объект, field_names содержит список загруженных полей, а values содержит значения полей из field_names. Поля в field_names расположены в таком же порядке как и values, поэтому можно использовать cls(**(zip(field_names, values))) для создания объекта. Если все поля модели присутствуют, порядок значений в values будет таким, каким их ожидает __init__(). И вы можете создать объект с помощью cls(*values). Все поля модели загружены, если cls._deferred равен False.

Кроме создания экземпляра модели метод from_db() должен установить флаги adding и db атрибута _state нового объекта модели.

В этом примере вы можете увидеть как сохранить начальные загруженные значений полей и проверять их при сохранении:

@classmethod
def from_db(cls, db, field_names, values):
    # default implementation of from_db() (could be replaced
    # with super())
    if cls._deferred:
        instance = cls(**zip(field_names, values))
    else:
        instance = cls(*values)
    instance._state.adding = False
    instance._state.db = db
    # customization to store the original field values on the instance
    instance._loaded_values = zip(field_names, values)
    return instance

def save(self, *args, **kwargs):
    # Check how the current values differ from ._loaded_values. For example,
    # prevent changing the creator_id of the model. (This example doesn't
    # support cases where 'creator_id' is deferred).
    if not self._state.adding and (
            self.creator_id != self._loaded_values['creator_id']):
        raise ValueError("Updating the value of creator isn't allowed")
    super(...).save(*args, **kwargs)

Это оригинальная реализация метода from_db(), чтобы показать как он работает. На самом деле мы бы могли просто вызвать родительский метод через super().

Обновление объектов из базы данных

Model.refresh_from_db(using=None, fields=None, **kwargs)

Добавлено в Django 1.8.

Если вам необходимо обновить значения модели из базы данных, вы можете использовать refresh_from_db(). Если вызвать этот метод без аргументов, произойдет следующее:

  1. Все загруженные поля модели будут обновлены значениями из базы данных.

  2. Если ранее загруженные внешние связи уже не верны, они будут удалены. Например, если объект содержит внешнюю связь на модель Author, и obj.author_id != obj.author.id, obj.author будет очищен и при следующем обращении будет загружен объект со значением obj.author_id.

Обратите внимание, загружаются только поля модели. Аннотации и прочие специфические значения не будут загружены.

Перезагрузка выполняется из базы данных, из которой объект был изначально загружен, или из базы данных по умолчанию, если база данных не была явно указана. Аргумент using позволяет явно указать базу данных.

Вы можете указать какие поля загружать с помощью аргумента fields.

Например, чтобы проверить вызов метода update(), вы можете использовать следующий тест:

def test_update_result(self):
    obj = MyModel.objects.create(val=1)
    MyModel.objects.filter(pk=obj.pk).update(val=F('val') + 1)
    # At this point obj.val is still 1, but the value in the database
    # was updated to 2. The object's updated value needs to be reloaded
    # from the database.
    obj.refresh_from_db()
    self.assertEqual(obj.val, 2)

Обратите внимание, при доступе к отложенным(deferred) полям они загружаются этим методом. Таким образом вы можете переопределить способ загрузки отложенных полей. В этом примере мы перегружаем все поля, если загружаются отложенные поля:

class ExampleModel(models.Model):
    def refresh_from_db(self, using=None, fields=None, **kwargs):
        # fields contains the name of the deferred field to be
        # loaded.
        if fields is not None:
            fields = set(fields)
            deferred_fields = self.get_deferred_fields()
            # If any deferred field is going to be loaded
            if fields.intersection(deferred_fields):
                # then load all of them
                fields = fields.union(deferred_fields)
        super(ExampleModel, self).refresh_from_db(using, fields, **kwargs)
Model.get_deferred_fields()

Добавлено в Django 1.8.

Возвращает список текущих отложенных полей для экземпляра модели.

Проверка объектов

Проверка объектов модели проходив в три этапа:

  1. Проверка полей модели — Model.clean_fields()

  2. Проверка всего объекта — Model.clean()

  3. Проверка уникальности полей — Model.validate_unique()

Все три этапа выполняются при вызове метода full_clean().

При использовании ModelForm, вызов is_valid() выполняет проверку для всех полей, включенных в форму. Подробности смотрите раздел о ModelForm. Вы должны использовать метод full_clean() модели только если собираетесь самостоятельно обрабатывать ошибки валидности, или если ModelForm не содержит поля, которые должны проверяться.

Model.full_clean(exclude=None, validate_unique=True)

Этот метод вызывает Model.clean_fields(), Model.clean(), и Model.validate_unique()`(если «validate_unique`() равно True) в указанном порядке и вызывает исключение ValidationError, которое содержит атрибут message_dict с ошибками всех трех этапов проверки.

Необязательный аргумент exclude используется, чтобы исключить часть полей из проверки. ModelForm использует этот аргумент для полей не включенных в форму, так как ошибки для этих полей не могут быть исправлены пользователем.

Обратите внимание, full_clean() не вызывается при вызове метода save(). Если вы хотите выполнить проверку для созданных вами объектов модели, вам необходимо явно вызывать этот метод. Например:

from django.core.exceptions import ValidationError
try:
    article.full_clean()
except ValidationError as e:
    # Do something based on the errors contained in e.message_dict.
    # Display them to a user, or handle them programmatically.
    pass

Первым делом full_clean() выполняет проверку каждого поля.

Model.clean_fields(exclude=None)

Этот метод проверяет все поля модели. Необязательный аргумент exclude используется, чтобы исключить часть полей из проверки. Если одно из полей не пройдет проверку, будет вызвано исключение ValidationError.

Следующим этапов проверки в full_clean() будет вызов метода Model.clean(). Этот метод должен быть переопределен, если вам нужна дополнительная проверка модели.

Model.clean()

Этот метод должен быть переопределен, если вам нужна дополнительная проверка модели или изменить значения атрибутов. Для объектов, вы можете определить его для автоматического определения полей, или для проверки, которая требует значения нескольких полей:

import datetime
from django.core.exceptions import ValidationError
from django.db import models

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError('Draft entries may not have a publication date.')
        # Set the pub_date for published items if it hasn't been set already.
        if self.status == 'published' and self.pub_date is None:
            self.pub_date = datetime.date.today()

Обратите внимание, Model.full_clean(), как и метод модели clean(), не вызываются при вызове save().

Любое исключение ValidationError вызванное в Model.clean() будет сохранено со специальным ключом в словаре ошибок, NON_FIELD_ERRORS, который используется для ошибок относящихся ко всей модели, а не конкретному полю:

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
    article.full_clean()
except ValidationError as e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

Чтобы добавить ошибку валидации к определенному полю, создайте экземпляр ValidationError со словарем, где ключи отображают необходимые поля. Добавим к предыдущему примеру проверку поля pub_date:

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError({'pub_date': 'Draft entries may not have a publication date.'})
        ...

Также full_clean() выполняет все проверки на уникальность модели.

Model.validate_unique(exclude=None)

Этот метод похож на clean_fields(), но проверяет уникальность полей, используя все определенные правила, а не значения полей.Необязательный аргумент exclude используется, чтобы исключить часть полей из проверки. Вызывает исключение ValidationError, если поле не прошло проверку.

Заметим, при использовании аргумента exclude, все правила определенные в unique_together, включающие одно из указанных полей, не будет учитываться при проверке.

Сохранение объектов

Чтобы сохранить объект в базе данных, используйте save():

Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None])

Если вы хотите изменить процесс сохранения, переопределите метод save(). Подробности в разделе Переопределение методов модели.

Процесс сохранения модели имеет ряд особенностей описанных ниже.

Автоинкрементные первичные ключи

Если модель содержит AutoField — автоинкрементный первичный ключи — его значение будет вычислено и сохранено в атрибут объекта при первом вызове метода save():

>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id     # Returns None, because b doesn't have an ID yet.
>>> b2.save()
>>> b2.id     # Returns the ID of your new object.

Нельзя точно сказать каким будет значение ID до вызова метода save(), так как оно вычисляется базой данных, а не Django.

Для удобства каждая модель содержит полей AutoField с названием id, если вы не указали параметр primary_key=True для поля модели. Подробности в описании AutoField.

Свойство pk
Model.pk

Независимо от того, определили вы первичный ключ самостоятельно, или позволили Django добавить его, каждая модель содержит свойство pk. Он ведет себя как обычный атрибут, но на самом деле является псевдонимом для атрибута первичного ключа. Вы можете получить или установить его значение, так же как и любого другого атрибута, при этом будет обновлено соответствующее поле модели.

Явное определение значения первичного ключа

Если модель содержит AutoField но вы хотите явно указать значение ID нового объекта при сохранении, просто укажите его:

>>> b3 = Blog(id=3, name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b3.id     # Returns 3.
>>> b3.save()
>>> b3.id     # Returns 3.

Если вы определяете значение первичного автоинкрементного ключа, убедитесь что это значение не существует уже в базе данных! Если вы укажите существующее в базе значение, Django предположит что вы хотите изменить запись в базе данных, а не сохранить новую.

Учитывая пример с блогом ‘Cheddar Talk’ выше, этот код перезапишет предыдущий объект в базе данных:

b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.')
b4.save()  # Overrides the previous blog with ID=3!

О том, как это определяется, смотрите How Django knows to UPDATE vs. INSERT (FIXME) ниже.

Явное определение первичного ключа в основном полезно при сохранении множества объектов, когда вы уверенны что все значения уникальны.

Что происходит при сохранении?

При сохранении объекта Django выполняет следующие шаги:

  1. Посылается сигнал pre-save. Посылается сигнал django.db.models.signals.pre_save, позволяя функциям, обрабатывающим этот сигнал, выполнить какие-либо действия.

  2. Предварительная обработка данных. Каждое поле объекта выполняет изменения значения поля при необходимости.

    Большинство полей не выполняют предварительную обработку данных — значение полей сохраняется как оно есть. Она выполняется для полей с особым поведением. Например, если ваша модель содержит поле DateField с auto_now=True, на этапе предварительной обработки значение поля будет установлено в текущую дату. (Наша документация пока не содержит список полей с “особым поведением”.)

  3. Подготовка данных для базы данных. Каждое поле преобразует текущее значение к типу данных, которой может быть сохранен в базу данных.

    Большинство полей не требует подготовки данных. Простые типы данных, такие как числа и строки, уже ‘готовы к сохранению’ как объекты Python. Однако, большинство сложных типов данных требуют некоторой модификации.

    Например, поле DateField использует объект Python datetime для хранения значения. База данных не принимает объект datetime, поэтому значение поля должно быть преобразовано в строковое представление даты в соответствии стандарту ISO перед сохранением в базу данных.

  4. Сохранение данных в базе данных. Предварительно обработанные, подготовленные данные формируются в SQL запрос, который выполняется в базе данных.

  5. Посылается сигнал post-save. Посылается сигнал django.db.models.signals.post_save, позволяя функциям, обрабатывающим этот сигнал, выполнить какие-либо действия.

Как Django определят использовать UPDATE или INSERT

Вы уже заметили что объекты модели используют метод save() как для создания так и для изменения записи в базе данных. Django самостоятельно определяет использовать INSERT или UPDATE. При вызове save(), Django следует такому алгоритму:

  • Если атрибут первичного ключа объекта содержи значение равное True (например, не None или не пустая строка), Django выполняет UPDATE запрос.

  • Если первичный ключ не указан, или UPDATE ничего не обновил, Django выполнит INSERT.

Будьте осторожны, явно указывая значение первичного ключа при сохранении нового объекта, если вы не уверенны, что этот первичный ключ не используется. Более подробно об этом читайте Explicitly specifying auto-primary-key values (FIXME) и Принудительное выполнение INSERT или UPDATE.

В предыдущих версиях Django выполнялся SELECT запрос, когда первичный ключ был явно указан. Если SELECT находил строку, Django выполнял UPDATE запрос, иначе – INSERT. В результате выполнялся один лишний запрос в случае использования UPDATE. В очень редких случаях база данных не оповещает об обновлении записи, если уже есть с указанным первичным ключом. Например, тригер PostgreSQL ON UPDATE, который возвращает NULL. В таких случаях можно вернуться к старому алгоритму, указав True в select_on_save.

Принудительное выполнение INSERT или UPDATE

В редких случаях, может понадобиться принудительно заставить метод save() выполнить INSERT запрос вместо UPDATE. Или наоборот: обновить, при возможности, но не добавлять новую запись. В этом случае вы можете указать аргумент force_insert=True или force_update=True для метода save(). Очевидно, не правильно использовать оба аргумента вместе: вы не можете добавлять и обновлять одновременно!

Вряд ли вам понадобится использовать эти параметры. Django почти всегда сделает то, что вам нужно, и переопределение такого поведения может привести к ошибкам, которые трудно отследить. Эта функция предназначена для опытных пользователей.

Использование update_fields инициирует обновление объекта, как и при вызове force_update.

Обновление значений полей

Иногда вам может понадобиться выполнить простые арифметические операции над полями, такие как увеличить или уменьшить текущее значение. Очевидный способ сделать это:

>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold += 1
>>> product.save()

Если старое значение number_sold, полученное из базы данных, равно 10, в базу данных будет записано значение 11.

Этот процесс может быть надежным, предотвращая состояния гонки, и немного быстрее, если выполнить обновление значение поля, а не явное присвоение нового значения. Django предоставляет объект F для выполнения обновления. Используя F(), следующий пример будет выглядеть таким образом:

>>> from django.db.models import F
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.save()

Подробности смотрите в описании объекта F и его использование в запросах обновления.

Указываем какие поля сохранять

Если в save() передать именованный аргумент update_fields со списком полей модели, только эти поля будут обновлены. Это может пригодиться, если вы хотите обновить одно или несколько полей. Таким образом можно получить небольшой прирост в производительности. Например:

product.name = 'Name changed again'
product.save(update_fields=['name'])

update_fields может принимать любой итератор строк. Пустой update_fields пропустит сохранение. None сохранит все поля.

Указав update_fields вы инициируете редактирование записи.

Если модель была загружена не со всеми полями (через only() или defer()), только загруженные поля будут сохранены. В этом случае update_fields будет определен автоматически. Если значение поля будет изменено, оно будет добавлено в список для обновления.

Удаление объектов

Model.delete([using=DEFAULT_DB_ALIAS])

Выполняет SQL DELETE запрос для объекта. Удаляет объекты только из базы данных; объекты Python будут существовать и содержать данные.

Подробности, включая как удалить множество объектов, смотрите в Удаление объектов.

Если вам нужно изменить процесс удаления, переопределите метод delete(). Подробности в Переопределение методов модели.

Сериализация объектов

При упаковке объекта модели с помощью pickle сохраняется текущее состояние. При распаковке объекта он содержит объект модели в состоянии на момент упаковки, а не текущее состояние в базе данных.

Вы не можете использовать объекты, упакованные на разных версиях Django

Упакованные модели работают только для той версии Django, на которой они были созданы. Если вы упаковали модель на Django версии N, нет гарантии, что вы сомжете распаковать её на Django версии N+1. Упаковка моделей не должна использоваться для долговременного архивирования объектов.

Добавлено в Django 1.8.

Ошибки несовместимости версий при распаковке объектом тяжело диагностировать, например сломанный объект, поэтому вызывается RuntimeWarning при попытке распаковать модель на версии Django отличной от той, которая использовалась при упаковке.

Остальные методы модели

Несколько методов имеют специальное назначение.

Примечание

В Python 3, так как все строки являются Unicode строками, используйте только метод __str__() (метод __unicode__() устарел). Если вам необходима совместимость с Python 2, Можете декорировать ваш класс модели декоратором python_2_unicode_compatible().

__unicode__

Model.__unicode__()

Метод __unicode__() вызывается когда вы применяете функцию unicode() к объекту. Django использует unicode(obj) (или похожую функцию str(obj)) вы нескольких местах. В частности, для отображения объектов в интерфейсе администратора Django и в качестве значения, вставляемого в шаблон, при отображении объекта. Поэтому, вы должны всегда возвращать в методе __unicode__() красивое и удобное для восприятия представление объекта.

Например:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def __unicode__(self):
        return u'%s %s' % (self.first_name, self.last_name)

Если вы определили метод __unicode__() и не определили __str__(), Django самостоятельно добавит метод __str__() который вызывает __unicode__(), затем преобразует результат в строку в кодировке UTF-8. Это рекомендуемый подход: определить только __unicode__() и позволить Django самостоятельно преобразовать в строку при необходимости.

__str__

Model.__str__()

Метод __str__() вызывается когда вы применяете функцию str() к объекту. В Python 3 Django использует str(obj) в нескольких местах. В частности, для отображения объектов в интерфейсе администратора Django и в качестве значения, вставляемого в шаблон, при отображении объекта. Поэтому, вы должны всегда возвращать в методе __str__() красивое и удобное для восприятия представление объекта.

Например:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def __str__(self):
        return '%s %s' % (self.first_name, self.last_name)

В Python 2 Django использует __str__, если нужно вывести результат функции repr() (например, при отладке). Определять метод __str__() не обязательно, если вы определили метод __unicode__().

Предыдущий пример метода __unicode__() может аналогично использоваться и в __str__():

from django.db import models
from django.utils.encoding import force_bytes

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def __str__(self):
        # Note use of django.utils.encoding.force_bytes() here because
        # first_name and last_name will be unicode strings.
        return force_bytes('%s %s' % (self.first_name, self.last_name))

__eq__

Model.__eq__()

Метод проверки равенства по умолчанию работает следующим образом: если два объекта содержат одинаковый первичный ключ и являются экземплярами одно класса, тогда они равны. Для прокси-моделей класс определяется поиском первого не прокси родительского класса. Для всех остальных моделей — это просто класс модели.

Например:

from django.db import models

class MyModel(models.Model):
    id = models.AutoField(primary_key=True)

class MyProxyModel(MyModel):
    class Meta:
        proxy = True

class MultitableInherited(MyModel):
    pass

MyModel(id=1) == MyModel(id=1)
MyModel(id=1) == MyProxyModel(id=1)
MyModel(id=1) != MultitableInherited(id=1)
MyModel(id=1) != MyModel(id=2)

Изменено в Django 1.7:

В предыдущих версиях только экземпляры одно класса с одинаковым первичным ключом считались равными.

__hash__

Model.__hash__()

Метод __hash__ использует значение первичного ключа. На самом деле выполняется hash(obj.pk). Если первичный ключ не определен, будет вызвано исключение TypeError (иначе __hash__ разные значения перед и после сохранения объекта, что запрещено в Python).

Изменено в Django 1.7:

В предыдущих версиях объекты без первичного ключа можно было хешировать.

get_absolute_url

Model.get_absolute_url()

Определите метод get_absolute_url(), чтобы указать Django как вычислить URL для объекта. Метод должен вернуть строку, которая может быть использована в HTTP запросе.

Например:

def get_absolute_url(self):
    return "/people/%i/" % self.id

(Хотя это код правильный и простой, но такой подход не самый лучший для создания подобных методов. Лучше использовать функцию reverse().)

Например:

def get_absolute_url(self):
    from django.core.urlresolvers import reverse
    return reverse('people.views.details', args=[str(self.id)])

Django использует get_absolute_url() в интерфейсе администратора. Если объект содержит этот метод, страница редактирования объекта будет содержать ссылку “Показать на сайте”, которая приведет к странице отображения объекта, ссылку на которую возвращает get_absolute_url().

Кроме того, несколько приложений Django также используют этот метод, например syndication feed framework. Если объект модели представляет какой-то уникальный URL, вам стоит определить метод get_absolute_url().

Предупреждение

При создания URL не используйте непроверенные данные от пользователя, чтобы избежать подделки ссылок или перенаправлений:

def get_absolute_url(self):
    return '/%s/' % self.name

Если self.name равен ‘/example.com’, будет возвращен ‘//example.com/’, являющимся правильным URL-ом относительно протокола, вместо ожидаемого ‘/%2Fexample.com/’.

Хорошая практика использовать get_absolute_url() в шаблонах, вместо того, чтобы “хардкодить” URL-ы. Например, это плохой подход:

<!-- BAD template code. Avoid! -->
<a href="/people/{{ object.id }}/">{{ object.name }}</a>

Этот шаблон значительно лучше:

<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>

Идея в том что, если вы измените структуру URL-а для объекта, или просто исправите опечатку, вам не нужно исправлять его во всех местах, где этот URL используется. Просто определите его один раз в методе get_absolute_url(), и пусть остальной код использует его.

Примечание

Строка, которую возвращает get_absolute_url(), должна состоять только из ASCII символов (требуется спецификацией URI, RFC 2396) и быть закодированной для URL, если необходимо.

Код и шаблоны, использующие get_absolute_url(), должны иметь возможность использовать результат без обработки. Вы можете использовать функцию django.utils.encoding.iri_to_uri(), если используете unicode-строку, которая содержит не ASCII символы.

Models. Part 3 / Хабр

Доброго времени суток!

Еще одна часть серии моих переводов раздела о моделях из документации Django.

Перевод Django Documentation: Models. Part 1
Перевод Django Documentation: Models. Part 2

___Мета-параметры

___Методы моделей

_____Переопределение предопределенных методов

_____Использование SQL

___Наследование моделей

_____Абстрактные базовые классы

_______Мета-наследование

_______Будьте аккуратны с related_names

Перевод Django Documentation: Models. Part 4 (Last)

Мета-параметры

Добавить вашей модели мета-данные вы можете с помощью внутреннего класса Meta:

Copy Source | Copy HTML<br/>class Ox(models.Model):<br/>    horn_length = models.IntegerField()<br/> <br/>    class Meta:<br/>        ordering = ["horn_length"]<br/>        verbose_name_plural = "oxen" <br/>

Мета-данные в модели это «что-либо не являющееся полем», например: параметры сортировки (ordering), имя таблицы базы данных (db_table), читабельные имена объектов в единственном (verbose_name) или множественном (verbose_name_plural) числе и тд. Мета-данные не являются обязательными, добавление класса Meta в вашу модель полностью опционально.

Полный список мета-параметров вы можете найти в справке по параметрам модели.

Методы моделей

Вы можете определить в модели методы, чтобы добавить собственной функциональности вашим объектам. В то время как методы Manager предназначены для работы с данными всей таблицы, методы модели созданы для действий в отдельном экземпляре модели.

Эта техника полезна для хранения бизнес-логики приложения в одном месте — в модели.

Например, эта модель имеет несколько собственных методов:

Copy Source | Copy HTML<br/>from django.contrib.localflavor.us.models import USStateField<br/> <br/>class Person(models.Model):<br/>    first_name = models.CharField(max_length=50)<br/>    last_name = models.CharField(max_length=50)<br/>    birth_date = models.DateField()<br/>    address = models.CharField(max_length=100)<br/>    city = models.CharField(max_length=50)<br/>    state = USStateField() # Yes, this is America-centric...<br/> <br/>    def baby_boomer_status(self):<br/>        "Returns the person's baby-boomer status."<br/>        import datetime<br/>        if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31):<br/>            return "Baby boomer"<br/>        if self.birth_date < datetime.date(1945, 8, 1):<br/>            return "Pre-boomer"<br/>        return "Post-boomer"<br/> <br/>    def is_midwestern(self):<br/>        "Returns True if this person is from the Midwest."<br/>        return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')<br/> <br/>    def _get_full_name(self):<br/>        "Returns the person's full name."<br/>        return '%s %s' % (self.first_name, self.last_name)<br/>    full_name = property(_get_full_name) <br/>

Последний метод этом примере — управляемый атрибут (property). Более подробно здесь.

В справке по экземплярам моделей содержится полный список автоматически добавляемых каждой модели методов. Вы можете переопределить большую их часть (об этом ниже). Также есть пара методов, которые переопределяют в большинстве случаев:

__unicode__()

«Магический метод» языка Python, который возвращает «представление» любого объекта в кодировке unicode. Его используют, когда требуется отобразить экземпляр модели в виде простой, понятной строки. Например, при работе в интерактивной консоли или в интерфейсе администратора.

Переопределение этого метода требуется довольно часто, потому что стандартный не очень полезен.

get_absolute_url()

Этот метод сообщает Django алгоритм вычисления URL для объекта. Он используется в интерфейсе администратора, а также каждый раз, когда нужно выяснить URL некоторого объекта.

Каждый объект, который имеет однозначно определяемый URL, должен содержать данный метод.

Переопределение предопределенных методов

Существует еще несколько методов моделей, с помощью которых вы можете изменять поведение базы данных. В частности, нередко требуется изменение алгоритма работы методов save() и delete().

Вы можете переопределять эти методы (так же как и все остальные в модели), и тем самым менять их поведение.

Например, переопределение встроенных методов используется, если вы хотите, чтобы при сохрании объекта совершались некоторые действия (более подробно о принимаемых методом save() параметрах вы можете прочитать в документации):

Copy Source | Copy HTML<br/>class Blog(models.Model):<br/>    name = models.CharField(max_length=100)<br/>    tagline = models.TextField()<br/> <br/>    def save(self, force_insert=False, force_update=False):<br/>        do_something()<br/>        super(Blog, self).save(force_insert, force_update) # The "real" save() method.<br/>        do_something_else()

Также вы можете предотвратить сохранение:

Copy Source | Copy HTML<br/>class Blog(models.Model):<br/>    name = models.CharField(max_length=100)<br/>    tagline = models.TextField()<br/> <br/>    def save(self, force_isert=False, force_update=False):<br/>        if self.name == "Yoko Ono's blog":<br/>            return # Yoko shall never have her own blog!<br/>        else:<br/>            super(Blog, self).save(force_insert, force_update) # The "real" save() method.<br/>

Важно не забыть вызвать метод наследуемого класса (в нашем примере super(Blog, self).save()), чтобы гарантировать сохранение данных в базу данных Если же вы забудете вызвать этот метод, то база данных останется нетронутой.

Использование SQL

Еще один частоиспользуемый прием заключается в написании собственных конструкций SQL в методах модели или в методах модуля. Более подробно об использовании «сырого» SQL вы можете прочитать в документации.

Наследование моделей

добавлено в версии Django 1.0: пожалуйста, прочтите примечания к релизу.

Принцип наследования моделей в Django почти полностью идентичен наследованию классов в языке Python. Единственное решение, которое вам придется принимать это, будут ли наследуемые модели представлять собой таблицу базы данных, или же они будут являться лишь хранителями данных, и доступ к ним будет возможен только через производные модели.

В Django существует три вида наследования:

  1. Зачастую вам нужно, чтобы базовый класс просто содержал некую информацию, которую вы не хотите определять в каждой производной модели. Такой класс никогда не будет использоваться как самостоятельная единица, а это значит, что лучше всего вам подойдут Абстрактные базовые классы (о них рассказано ниже).
  2. Если при создании подклассов вы хотите, чтобы каждая модель имела собственную таблицу в базе данных, смотрите в сторону Мульти-табличного наследования.
  3. И наконец, если вы хотите изменить поведение модели на Python-уровне без изменения ее полей, используйте прокси-модели.
Абстрактные базовые классы

Абстрактные базовые классы полезны, когда вы хотите создать несколько моделей с общей информацией. Создайте базовый класс, поместите параметр abstract=True в класс Meta, и эта модель не будет использоваться при создании таблиц базы данных. Вместо этого, когда он будет использоваться как базовый для некоторой модели, его поля будут добавлены к производному классу. Если же имя поля в базовом и производном классе совпадут, Django возбудит исключение.

Пример:

Copy Source | Copy HTML<br/>class CommonInfo(models.Model):<br/>    name = models.CharField(max_length=100)<br/>    age = models.PositiveIntegerField()<br/> <br/>    class Meta:<br/>        abstract = True<br/> <br/>class Student(CommonInfo):<br/>    home_group = models.CharField(max_length=5) <br/>

Модель Student будет содержать три поля: name, age и home_group. Так как модель CommonInfo — абстрактный базовый класс, она не будет использоваться как обычная модель Django: она не будет генерировать таблицу базы данных, иметь manager, нельзя будет сохранить или создать ее экземпляр напрямую.

Во многих случаях этот тип наследования будет удовлетворять вашим требованиям. Данный метод позволяет «вынести за скобки» общую информацию на Python-уровне, в то время как на уровне базы данных будут создаваться отдельные таблицы для каждого производного класса.

Мета-наследование

Когда создается абстрактный базовый класс, Django делает внутренний класс Meta, который вы определили в базовом классе, доступным как атрибут. Если в производном классе не будет определен собственный класс Meta, он будет унаследован от родителя. Однако при желании вы можете расширить базовый класс Meta, использовав наследование в классах Meta. Например:

Copy Source | Copy HTML<br/>class CommonInfo(models.Model):<br/>    ...<br/>    class Meta:<br/>        abstract = True<br/>        ordering = ['name']<br/> <br/>class Student(CommonInfo):<br/>    ...<br/>    class Meta(CommonInfo.Meta):<br/>        db_table = 'student_info' <br/>

Django делает лишь одну корректировку в классе Meta абстрактного базового класса: перед установлением атрибута Meta, параметр abstract принмает значение False. Это делается для того, чтобы производные классы не становились автоматически абстрактными. Разумеется, вы можете создать абстрактный базовый класс, который наследуется от другого абстрактного базового класса. Для этого вам просто следует явно устанавливать abstract=True.

Не имеет смысла добавлять некоторые атрибуты в класс Meta абстрактного базового класса. Например, добавление параметра db_name, будет означать, что все производные классы, которые не имеют собственного класса Meta, будут использовать одну и ту же таблицу базы данных. Почти наверняка, это совсем не то чего вы хотели.

Будьте аккуратны с related_name

Если вы используете атрибут related_name в поле ForeignKey или ManyToManyField, вы должны определить уникальное обратное имя для каждого поля. Это обычно приводит проблеме в абстрактных базовых классах, так как все поля и их значения добавляются в каждый производный класс.

Чтобы обойти эту проблему, при использовании атрибута related_name в абстрактных базовых классах (и только в них), частью имени должна быть строка ‘%(class)s’. Она будет заменена на имя производного класса в нижем регистре, в котором используется это поле. Так как классы имеют различные имена, каждый атрибут будет иметь уникальное значение. Например:

Copy Source | Copy HTML<br/>class Base(models.Model):<br/>    m2m = models.ManyToManyField(OtherModel, related_name="%(class)s_related")<br/> <br/>    class Meta:<br/>        abstract = True<br/> <br/>class ChildA(Base):<br/>    pass<br/> <br/>class ChildB(Base):<br/>    pass <br/>

Обратное имя ChildA.m2m будет childa_related, а для ChildB.m2mchildb_related. Ваше дело как использовать конструкцию ‘%(class)s’, однако если вы забудете про нее, Django возбудит исключение при валидации ваших моделей (или при выполнении syncdb).

Если же вы не определили атрибут related_name для поля в абстрактном базовом классе, обратное имя по умолчанию будет именем производного класса в нижнем регистре с добавлением строки ‘_set’, так будто вы явно определили это поле непосредственно в производном классе. Например, для приведеного выше текста, если бы атрибут related_name был опущен, обратными именами для полей m2m были бы childa_set для класса ChildA и childb_set в случае с классом ChildB.

На сегодня все 🙂 Следущая часть будет последней в данном разделе. Думаю, после ее перевода создать опрос — нужны ли хабру эти самые переводы или нет. Буду ли я продолжать напрямую зависит от его результатов. А пока нужно завершить переводить то, что начал )

to be continued

Любые комментарии приветствуются 🙂

Перевод Django Documentation: Models. Part 1
Перевод Django Documentation: Models. Part 2
Перевод Django Documentation: Models. Part 4 (Last)

моделей Django · HonKit

Сейчас мы хотим создать то, что будет хранить все сообщения в нашем блоге. Но для этого нам нужно немного поговорить о вещах, называемых , объектами .

Объектов

В программировании существует концепция , объектно-ориентированное программирование . Идея состоит в том, что вместо того, чтобы писать все как скучную последовательность инструкций по программированию, мы можем моделировать вещи и определять, как они взаимодействуют друг с другом.

Так что же такое объект? Это набор свойств и действий. Звучит странно, но мы приведем вам пример.

Если мы хотим смоделировать кошку, мы создадим объект Cat , который имеет некоторые свойства, такие как цвет , возраст , настроение (например, хорошее, плохое или сонное;)) и owner (которому может быть присвоен объект Person — или, может быть, в случае бездомной кошки это свойство может быть пустым).

Затем Cat выполняет некоторые действия: мурлыкать , царапать или кормить (в этом случае мы дадим кошке примерно CatFood , который может быть отдельным объектом со свойствами, например вкусом ).

  Кот
--------
цвет
возраст
настроение
владелец
мурлыкать ()
царапина()
корм (cat_food)
  
  CatFood
--------
вкус
  

Итак, основная идея состоит в том, чтобы описывать реальные вещи в коде с помощью свойств (называемых свойств объекта ) и действий (называемых методами ).

Как тогда мы будем моделировать сообщения в блогах? Мы хотим создать блог, верно?

Нам нужно ответить на вопрос: что такое сообщение в блоге? Какие свойства у него должны быть?

Ну, конечно, нашему сообщению в блоге нужен текст с его содержанием и заголовком, верно? Также было бы неплохо знать, кто это написал — значит, нам нужен автор.Наконец, мы хотим знать, когда сообщение было создано и опубликовано.

  Пост
--------
заглавие
текст
автор
Дата создания
опубликованная_дата
  

Что можно сделать с сообщением в блоге? Было бы неплохо иметь какой-нибудь метод , который публикует пост, не так ли?

Итак, нам понадобится метод публикации .

Поскольку мы уже знаем, чего хотим достичь, давайте начнем моделировать это в Django!

Модель Django

Зная, что такое объект, мы можем создать модель Django для нашего сообщения в блоге.

Модель в Django — это особый вид объекта — он сохраняется в базе данных . База данных — это набор данных. Это место, в котором вы будете хранить информацию о пользователях, сообщениях вашего блога и т. Д. Мы будем использовать базу данных SQLite для хранения наших данных. Это стандартный адаптер базы данных Django — нам его сейчас хватит.

Вы можете представить себе модель в базе данных как электронную таблицу со столбцами (полями) и строками (данными).

Создание приложения

Чтобы все было в порядке, внутри нашего проекта создадим отдельное приложение.Очень приятно, что все организовано с самого начала. Чтобы создать приложение, нам нужно запустить следующую команду в консоли (из каталога djangogirls , где находится файл manage.py ):

Mac OS X и Linux:

  (myvenv) ~ / djangogirls $ python manage.py блог startapp
  

Windows:

  (myvenv) C: \ Users \ Name \ djangogirls> python manage.py блог startapp
  

Вы заметите, что создается новый каталог blog , а это

.

новых вопросов о моделях django — qaru

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *