Поиск…


Вступление

В базовом случае модель представляет собой класс Python, который сопоставляется с одной таблицей базы данных. Атрибуты карты классов для столбцов в таблице и экземпляр класса представляют строку в таблице базы данных. Модели наследуют от django.db.models.Model который предоставляет богатый API для добавления и фильтрации результатов из базы данных.

Создайте свою первую модель

Создание вашей первой модели

Модели обычно определяются в файле models.py в подкаталоге вашего приложения. Класс Model модуля django.db.models является хорошим стартовым классом для расширения ваших моделей. Например:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey('Author', on_delete=models.CASCADE, related_name='authored_books')
    publish_date = models.DateField(null=True, blank=True)

    def __str__(self): # __unicode__ in python 2.*
        return self.title

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

  • title - текст с максимальной длиной 100 символов
  • author - ForeignKey который представляет собой отношение к другой модели / таблице, в данном случае Author (используется только для примера). on_delete сообщает базе данных, что делать с объектом, если связанный объект ( Author ) будет удален. (Следует отметить, что поскольку django 1.9 on_delete может использоваться как второй позиционный аргумент. В django 2 это обязательный аргумент, и рекомендуется сразу же рассматривать его как таковое немедленно. В более старых версиях он будет по умолчанию для CASCADE .)
  • publish_date хранит дату. Оба null и blank равны True чтобы указать, что это не обязательное поле (т. Е. Вы можете добавить его позже или оставить пустым).

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

Применение изменений в базе данных (Миграции)

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

python manage.py makemigrations <appname>

Вышеприведенная команда создаст сценарии миграции, которые необходимы в подкаталоге migrations вашего приложения. Если вы опустите параметр <appname> , все приложения, определенные в аргументе INSTALLED_APPS параметра settings.py будут обработаны. Если вы сочтете это необходимым, вы можете редактировать миграцию.

Вы можете проверить, какие миграции требуются без фактического создания миграции, используйте параметр -dry-run, например:

python manage.py makemigrations --dry-run

Чтобы применить миграции:

python manage.py migrate <appname>

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

Если изменена модель существующей базы данных, для внесения необходимых изменений необходима следующая команда.

python manage.py migrate --run-syncdb

Django создаст таблицу с именем <appname>_<classname> по умолчанию. Иногда вы не хотите его использовать. Если вы хотите изменить имя по умолчанию, вы можете объявить имя таблицы, установив db_table в класс Meta :

from django.db import models

class YourModel(models.Model):
    parms = models.CharField()
    class Meta:
        db_table = "custom_table_name"

Если вы хотите увидеть, какой код SQL будет выполняться с помощью определенной миграции, просто запустите эту команду:

python manage.py sqlmigrate <app_label> <migration_number>

Джанго> 1.10
Новая опция makemigrations --check делает makemigrations --check команды с ненулевым статусом при обнаружении изменений модели без миграции.

См. « Миграции» для получения более подробной информации о миграции.

Создание модели с отношениями

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

from django.db import models

class Author(models.Model):
   name = models.CharField(max_length=50)

#Book has a foreignkey (many to one) relationship with author
class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    publish_date = models.DateField()

Самый общий вариант. Может использоваться везде, где вы хотели бы представить отношения

Отношения «многие ко многим»

class Topping(models.Model):
    name = models.CharField(max_length=50)

# One pizza can have many toppings and same topping can be on many pizzas
class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)

Внутренне это представлено через другую таблицу. И ManyToManyField следует помещать в модели, которые будут редактироваться в форме. Например: Appointment будет иметь ManyToManyField под названием Customer , Pizza имеет Toppings и так далее.

Использование отношений «многие-ко-многим» с использованием сквозных классов

class Service(models.Model):
     name = models.CharField(max_length=35)

class Client(models.Model):
    name = models.CharField(max_length=35)
    age = models.IntegerField()
    services = models.ManyToManyField(Service, through='Subscription')

class Subscription(models.Model):
     client = models.ForeignKey(Client)
     service = models.ForeignKey(Service)
     subscription_type = models.CharField(max_length=1, choices=SUBSCRIPTION_TYPES)
     created_at = models.DateTimeField(default=timezone.now)

Таким образом, мы можем сохранить больше метаданных о взаимосвязи между двумя объектами. Как видно, клиент может быть подписан на несколько сервисов несколькими типами подписки. Единственное отличие в этом случае заключается в том, что для добавления новых экземпляров в отношение M2M нельзя использовать метод ярлыков pizza.toppings.add(topping) , вместо этого должен быть создан новый объект класса through , Subscription.objects.create(client=client, service=service, subscription_type='p')

В других языках through tables также известны как JoinColumn , Intersection table или mapping table

Индивидуальные отношения

class Employee(models.Model):
   name = models.CharField(max_length=50)
   age = models.IntegerField()
   spouse = models.OneToOneField(Spouse)

class Spouse(models.Model):
   name = models.CharField(max_length=50)

Используйте эти поля, когда у вас будет только соотношение композиций между двумя моделями.

Основные запросы Django DB

Django ORM - это мощная абстракция, которая позволяет хранить и извлекать данные из базы данных без написания запросов sql.

Предположим следующие модели:

class Author(models.Model):
   name = models.CharField(max_length=50)

class Book(models.Model): 
   name = models.CharField(max_length=50)
   author = models.ForeignKey(Author)

Предполагая, что вы добавили вышеуказанный код в приложение django и запустите команду migrate (чтобы ваша база данных была создана). Запустите оболочку Django на

python manage.py shell

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

Начните с импорта только что определенных моделей (я предполагаю, что это делается в файле models.py )

from .models import Book, Author

Запустите свой первый запрос выбора:

>>> Author.objects.all() 
[]
>>> Book.objects.all()
[]

Позволяет создать объект автора и книги:

>>> hawking = Author(name="Stephen hawking")
>>> hawking.save()
>>> history_of_time = Book(name="history of time", author=hawking)
>>> history_of_time.save()

или использовать функцию create для создания объектов модели и сохранения в одной строке кода

>>> wings_of_fire = Book.objects.create(name="Wings of Fire", author="APJ Abdul Kalam")

Теперь запустите запрос

>>> Book.objects.all()
[<Book: Book object>]
>>> book = Book.objects.first() #getting the first book object
>>> book.name
u'history of time'

Давайте добавим предложение where к нашему запросу select

>>> Book.objects.filter(name='nothing')
[]
>>> Author.objects.filter(name__startswith='Ste')
[<Author: Author object>]

Чтобы получить подробную информацию об авторе данной книги

>>> book = Book.objects.first() #getting the first book object
>>> book.author.name # lookup on related model
u'Stephen hawking'

Чтобы получить все книги, опубликованные Стивеном Хокином (Lookup book от его автора)

>>> hawking.book_set.all()
[<Book: Book object>]

_set - это обозначение, используемое для «Обратного поиска», т. е. когда поле поиска находится в модели книги, мы можем использовать book_set для объекта автора, чтобы получить все его книги.

Основная неуправляемая таблица.

В какой-то момент использования Django вы можете захотеть взаимодействовать с уже созданными таблицами или с представлениями базы данных. В этих случаях вы не хотите, чтобы Django управлял таблицами через свои миграции. Чтобы установить это, вам нужно добавить только одну переменную в Meta класс вашей модели: managed = False .

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

class Dummy(models.Model):
    something = models.IntegerField()

    class Meta:
       managed = False

Это может быть сопоставлено с представлением, определенным в SQL, следующим образом.

CREATE VIEW myapp_dummy AS 
SELECT id, something FROM complicated_table 
WHERE some_complicated_condition = True

Когда вы создадите эту модель, вы можете использовать ее, как и любую другую модель:

>>> Dummy.objects.all()
[<Dummy: Dummy object>, <Dummy: Dummy object>, <Dummy: Dummy object>]
>>> Dummy.objects.filter(something=42)
[<Dummy: Dummy object>]

Расширенные модели

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

from django.db import models
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible
class Book(models.Model):
    slug = models.SlugField()
    title = models.CharField(max_length=128)
    publish_date = models.DateField()

    def get_absolute_url(self):
        return reverse('library:book', kwargs={'pk':self.pk})

    def __str__(self):
        return self.title

    class Meta:
        ordering = ['publish_date', 'title']

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

Вы можете заметить использование self.pk в методе get_absolute_url . Поле pk является псевдонимом первичного ключа модели. Кроме того, django автоматически добавит первичный ключ, если он отсутствует. Это еще одна вещь, о которой нужно беспокоиться, и дать вам возможность установить внешний ключ для любых моделей и получить их легко.

Абсолютный URL-адрес

Первая функция, которая определена, - get_absolute_url . Таким образом, если у вас есть книга, вы можете получить ссылку на нее без использования тэга url, разрешения, атрибута и тому подобного. Просто позвоните в book.get_absolute_url и вы получите правильную ссылку. В качестве бонуса ваш объект в администраторе django получит кнопку «просмотр на сайте».

Строковое представление

__str__ метод __str__ чтобы использовать объект, когда вам нужно его отобразить. Например, с помощью предыдущего метода добавление ссылки в книгу в шаблон так же просто, как <a href="{{ book.get_absolute_url }}">{{ book }}</a> . Прямо к сути. Этот метод также контролирует то, что отображается в раскрывающемся списке admin, например, для внешнего ключа.

Декоратор класса позволяет вам определить метод один раз для __str__ и __unicode__ на python 2, не вызывая проблем на python 3. Если вы ожидаете, что ваше приложение будет работать на обеих версиях, это путь.

Поле слизняков

Поле slug похоже на поле char, но принимает меньше символов. По умолчанию используются только буквы, цифры, символы подчеркивания или дефисы. Это полезно, если вы хотите идентифицировать объект, используя хорошее представление, например, в URL-адресе.

Класс Meta

Класс Meta позволяет нам определять намного больше информации по всему набору предметов. Здесь задается только порядок по умолчанию. Это полезно, например, для объекта ListView. Для сортировки требуется идеально короткий список полей. Здесь книга будет отсортирована сначала по дате публикации, а затем по названию, если дата будет одинаковой.

Другими атрибутами verbose_name являются verbose_name и verbose_name_plural . По умолчанию они генерируются из имени модели и должны быть точными. Но множественная форма наивна, просто добавляет 's' к единственному, поэтому вы можете явно указать его в некотором случае.

Вычисляемые значения

После того, как объект модели был извлечен, он становится полностью реализованным экземпляром класса. Таким образом, любые дополнительные методы могут быть доступны в формах и сериализаторах (например, в Django Rest Framework).

Использование свойств python - это элегантный способ представления дополнительных значений, которые не хранятся в базе данных из-за различных обстоятельств.

def expire():
    return timezone.now() + timezone.timedelta(days=7)

class Coupon(models.Model):
    expiration_date = models.DateField(default=expire)

    @property
    def is_expired(self):
        return timezone.now() > self.expiration_date

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

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

Добавление строкового представления модели

Чтобы создать удобочитаемое представление объекта модели, вам необходимо реализовать метод Model.__str__() (или Model.__unicode__() на python2). Этот метод будет вызываться всякий раз, когда вы вызываете str() в экземпляре вашей модели (включая, например, когда модель используется в шаблоне). Вот пример:

  1. Создайте модель книги.

    # your_app/models.py
    
    from django.db import models
    
    class Book(models.Model):
        name = models.CharField(max_length=50)
        author = models.CharField(max_length=50)
    
  2. Создайте экземпляр модели и сохраните ее в базе данных:

    >>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed')
    >>> himu_book.save()
    
  3. Выполните print() в экземпляре:

    >>> print(himu_book)
    <Book: Book object>
    

<Book: Объект Book> , выходной по умолчанию, не помогает нам. Чтобы исправить это, добавим метод __str__ .

from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible
class Book(models.Model):
    name = models.CharField(max_length=50)
    author = models.CharField(max_length=50)

    def __str__(self):
        return '{} by {}'.format(self.name, self.author)

Обратите внимание, что python_2_unicode_compatible decorator необходим, только если вы хотите, чтобы ваш код был совместим с python 2. Этот декоратор копирует метод __str__ для создания метода __unicode__ . Импортируйте его из django.utils.encoding .

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

>>> print(himu_book)
Himu Mama by Humayun Ahmed

Намного лучше!

Строковое представление также используется, когда модель используется в поля ModelForm для ForeignKeyField и ManyToManyField .

Модельные смеси

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

class PostableMixin(models.Model):
    class Meta:
        abstract=True
    
    sender_name = models.CharField(max_length=128)
    sender_address = models.CharField(max_length=255)
    receiver_name = models.CharField(max_length=128)
    receiver_address = models.CharField(max_length=255)
    post_datetime = models.DateTimeField(auto_now_add=True)
    delivery_datetime = models.DateTimeField(null=True)
    notes = models.TextField(max_length=500)

class Envelope(PostableMixin):
    ENVELOPE_COMMERCIAL = 1
    ENVELOPE_BOOKLET = 2
    ENVELOPE_CATALOG = 3

    ENVELOPE_TYPES = (
        (ENVELOPE_COMMERCIAL, 'Commercial'),
        (ENVELOPE_BOOKLET, 'Booklet'),
        (ENVELOPE_CATALOG, 'Catalog'),
    )

    envelope_type = models.PositiveSmallIntegerField(choices=ENVELOPE_TYPES)

class Package(PostableMixin):
    weight = models.DecimalField(max_digits=6, decimal_places=2)
    width = models.DecimalField(max_digits=5, decimal_places=2)
    height = models.DecimalField(max_digits=5, decimal_places=2)
    depth = models.DecimalField(max_digits=5, decimal_places=2)

Чтобы превратить модель в абстрактный класс, вам нужно будет указать abstract=True в своем внутреннем Meta классе. Django не создает таблицы для абстрактных моделей в базе данных. Однако для моделей Envelope и Package в базе данных будут созданы соответствующие таблицы.

Кроме того, в некоторых моделях потребуются некоторые модельные методы. Таким образом, эти методы могут быть добавлены в mixins для предотвращения повторения кода. Например, если мы создадим метод установки даты доставки в PostableMixin он будет доступен от обоих его дочерних элементов:

class PostableMixin(models.Model):
    class Meta:
        abstract=True

    ...
    ...

    def set_delivery_datetime(self, dt=None):
        if dt is None:
            from django.utils.timezone import now
            dt = now()

        self.delivery_datetime = dt
        self.save()

Этот метод можно использовать для детей следующим образом:

>> envelope = Envelope.objects.get(pk=1)
>> envelope.set_delivery_datetime()

>> pack = Package.objects.get(pk=1)
>> pack.set_delivery_datetime()

Первичный ключ UUID

Модель по умолчанию будет использовать первичный ключ auto incrementing (integer). Это даст вам последовательность клавиш 1, 2, 3.

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

UUID является универсально уникальным идентификатором, это 32-символьный случайный идентификатор, который может использоваться как идентификатор. Это хороший вариант использования, когда вы не хотите, чтобы в вашей базе данных были назначены последовательные идентификаторы. При использовании в PostgreSQL это хранится в виде данных uuid, иначе в char (32).

import uuid
from django.db import models

class ModelUsingUUID(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

Сгенерированный ключ будет в формате 7778c552-73fc-4bc4-8bf9-5a2f6f7b7f47

наследование

Наследование между моделями можно сделать двумя способами:

  • общий абстрактный класс (см. пример «Модельные миксины»)
  • общая модель с несколькими таблицами

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

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

создаст 2 таблицы для Place и один для Restaurant со скрытым OneToOne полем Place для общих полей.

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



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow