В целом для названий моделей рекомендуется использовать существительные в единственном числе. Например, User
, Post
, Article
. То есть, существительным должен быть последний компонент названия, как в Some New Shiny Item
. Единственное число уместно, когда один юнит модели не содержит информацию о нескольких объектах.
Связям (relationships), таким как «один ко многим» (ForeignKey
), «один к одному» (OneToOneKey
), «многие ко многим» (ManyToMany
), порой лучше давать более специфические имена.
Представьте, что у вас есть модель с именем Article
, в которой одна из связей это ForeignKey
для модели User
. Если это поле содержит информацию об авторе статьи, тогда имя Author
будет более подходящим, чем User
.
Поскольку related_name
возвращает queryset
, будет разумным использовать для него множественное число. Пожалуйста, используйте соответствующие related_name
. В большинстве случаев подходящим выбором будет имя самой модели во множественном числе. Например:
class Owner(models.Model): pass class Item(models.Model): owner = models.ForeignKey(Owner, related_name='items')
Нет никакого смысла использовать ForeignKey
с unique=True
, поскольку для таких случаев есть OneToOneField
.
Предпочтительный порядок указания атрибутов и методов в модели:
def __unicode__
(python 2) или def __str__
(python 3) def clean
def save
def get_absolut_url
Этот список был составлен на основе рекомендуемого в документации и слегка расширен.
Допустим, вам нужно добавить модель. Имея готовый класс модели, выполните серию команд manage.py: makemigrations
и migrate
(для Django 1.6 и ниже используйте South
).
Не следует допускать бездумную денормализацию в реляционных базах данных. Всегда старайтесь избегать этого, за исключением случаев, когда вам по каким-то причинам (например, для повышения производительности) приходится постоянно денормализовывать данные.
Если на этапе проектирования базы данных вы понимаете, что многие данные придется денормализовывать, хорошим вариантом будет использование NoSQL. Однако, если большинство данных не требуют непременной денормализации, подумайте о реляционной базе с JsonField
для хранения некоторых данных.
Не используйте null=True
или blank=True
для BooleanField
. Также следует отметить, что дефолтные значения для подобных полей лучше конкретизировать. Если вы понимаете, что поле может остаться пустым, используйте NullBooleanField
.
Бизнес-лоигку проекта лучше всего размещать в моделях, а именно — в методах и менеджере модели. Допустимо делать так, чтобы методы модели только запускали какие-то методы/функции. Если разместить логику в моделях неудобно или невозможно, следует перенести ее в формы.
Не следует без необходимости дублировать поля модели в ModelForm
или ModelSerializer
. Если вы хотите указать, что форма использует все поля модели, примените MetaFields
. Если вам нужно переопределить виджет для поля, а в самом поле ничего меняться не будет, используйте атрибут widgets
класса Meta
.
Использование ModelName.DoesNotExist
вместо ObjectDoesNotExist
сделает ваш перехват исключений более точным. Это хорошая практика.
При использовании choices
рекомендуется следующее:
new
, in_progress
, completed
). Choices
из библиотеки model_utils
. Возьмите, к примеру, модель Article
: from model_utils import Choices class Article(models.Model): STATUSES = Choices( (0, 'draft', _('draft')), (1, 'published', _('published')) ) status = models.IntegerField(choices=STATUSES, default=STATUSES.draft) …
Используя ORM, не добавляйте дополнительный вызов метода all
перед filter()
, count()
и т. п.
Если это оправдано, замените несколько BooleanField
одним полем, например:
class Article(models.Model): is_published = models.BooleanField(default=False) is_verified = models.BooleanField(default=False) …
Допустим, логика нашего приложения предполагает, что в «исходном положении» статья не опубликована и не проверена. Затем она проверяется (при этом is_verified
помечается как True
), а затем публикуется. То есть, статья не может быть опубликована, пока она не проверена.
В общей сложности у нас получается 3 условия. Но с 2 булевыми полями у нас нет 4 возможных вариантов. Кроме того, нужно следить за тем, чтобы не было статей с неправильными комбинациями условий в BooleanField
. Поэтому лучшим вариантом будет использовать не два BooleanField
, а одно поле статуса:
class Article(models.Model): STATUSES = Choices('new', 'verified', 'published') status = models.IntegerField(choices=STATUSES, default=STATUSES.draft) …
Этот пример, быть может, не слишком показательный, но представьте, что у вас в модели таких BooleanField
три или даже больше. Контролировать проверку комбинаций значений этих полей может быть довольно утомительным делом.
Не добавляйте имена моделей в имена полей, если в этом нет необходимости. Например, если в таблице User
есть поле user_status
, и никаких других ххх_status
в этой модели нет, это поле стоит переименовать в просто status
.
Всегда используйте PositiveIntegerField
, а не IntegerField
(конечно, там, где это не будет бессмысленным), потому что «плохие» данные не должны попадать в базу. По той же причине следует всегда использовать unique
, unique_together
для логически уникальных данных и ни в коем случае не ставить required=False
в каждом поле.
Вы можете использовать ModelName.objects.earliest('created'/'earliest')
вместо order_by('created')[0]
. Также можно поместить get_latest_by
в Meta
модели. Но следует иметь в виду, что latest/earliest
, как и get
, могут привести к появлению исключения DoesNotExist
. Таким образом, самым полезным вариантом будет order_by('created').first()
.
Не используйте len
для получения количества объектов в queryset
. Для этой цели можно использовать метод count
.
Например, если вы напишете len(ModelName.objects.all ())
, сначала будет выполнен запрос на выбор всех данных из таблицы, затем эти данные будут преобразованы в объект Python, а затем при помощи len
будет найдена длина этого объекта. Этот метод не рекомендуется использовать. Если вы примените count
, у вас будет выполняться более простой запрос к базе данных, и код станет более производительным.
Не используйте queryset
в качестве логического значения. Вместо if queryset: сделать_что-то
используйте if queryset.exists(): сделать_что-то
. Помните, что queryset-ы ленивы. Если использовать их в качестве булевых значений, будут выполняться неправильные запросы к базе данных.
Использование модели help_text
в полях в качестве части документации существенно облегчит понимание структуры данных. Это принесет пользу и вам, и вашим коллегам, и пользователям-админам.
Не используйте FloatField
для хранения сведений о количестве денег. Для этой цели следует пользоваться DecimalField
. Также вы можете хранить эти сведения в центах, юнитах и т. п.
null=True
— позволяет столбцу хранить значение null.
blank=True
— должно использоваться только для валидации в формах и там, где это не будет связано с работой с базой данных.
В текстовых полях лучше оставлять значение default
.
blank=True default=''
Таким образом вы будете получать только одно возможное значение для столбцов без данных.
Не добавляйте суффикс _id
к ForeignKeyField
и OneToOneField
.
Во всех неабстрактных моделях добавляйте методы __unicode__
(python 2) или __str__
(python 3). Эти методы должны всегда возвращать строки.
Не используйте Meta.exclude
для описания списка полей модели в ModelForm
. Для этого лучше использовать Meta.fields
, поскольку это делает список полей прозрачным. По той же причине не следует использовать Meta.fields=”__all__”
.
Если ожидается загрузка большого количества файлов, порой будет недостаточным даже иметь отдельные папки для всех FileField
. Если очень много файлов хранится в одной папке, при поиске каждого отдельного файла система будет работать медленнее. Чтобы этого избежать, можно поступить следующим образом:
def get_upload_path(instance, filename): return os.path.join('account/avatars/', now().date().strftime("%Y/%m/%d"), filename) class User(AbstractUser): avatar = models.ImageField(blank=True, upload_to=get_upload_path)
Если вы хотите, чтобы какая-то логика у моделей была общей, можно использовать абстрактные модели.
class CreatedatModel(models.Model): created_at = models.DateTimeField( verbose_name=u"Created at", auto_now_add=True ) class Meta: abstract = True class Post(CreatedatModel): ... class Comment(CreatedatModel): ...
Чем крупнее проект, над которым вы работаете, тем чаще вы будете повторять какой-то код в разных местах. Чтобы распределить бизнес-логику в моделях и при этом придерживаться принципа DRY, можно применить пользовательские Manager
и QuerySet
.
Допустим, вы хотите вести подсчет комментариев к постам из приведенного выше примера.
class CustomManager(models.Manager): def with_comments_counter(self): return self.get_queryset().annotate(comments_count=Count('comment_set'))
Вы можете использовать следующий подход:
posts = Post.objects.with_comments_counter() posts[0].comments_count
Если вы хотите использовать этот метод в связке с другими методами queryset, стоит применить пользовательский QuerySet
:
class CustomQuerySet(models.query.QuerySet): """Substitution the QuerySet, and adding additional methods to QuerySet """ def with_comments_counter(self): """ Adds comments counter to queryset """ return self.annotate(comments_count=Count('comment_set'))
Теперь вы можете написать так:
posts = Post.objects.filter(...).with_comments_counter() posts[0].comments_count
Pydantic - это мощная библиотека проверки данных и управления настройками для Python, созданная для повышения…
Python предлагает набор библиотек, удовлетворяющих различные потребности в визуализации, будь то академические исследования, бизнес-аналитика или…
В Python для представления данных в двоичной форме можно использовать байты. Из этой статьи вы…
В этой статье рассказывается о том, что такое Werkzeug и как Flask использует его для…
При работе с датами часто возникает необходимость прибавлять к дате или вычитать из нее различные…
В этом руководстве мы рассмотрим, как добавить социальную аутентификацию с помощью GitHub и Google в…