Pull to refresh

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

Reading time 9 min
Views 13K
image

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

Еще одна часть серии моих переводов раздела о моделях из документации 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)
Tags:
Hubs:
+23
Comments 17
Comments Comments 17

Articles