Разное

Foreignkey django: Many-to-one relationships | Django documentation

Содержание

Django учебник Часть 3: Использование моделей — Изучение веб-разработки

             

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

Веб-приложения Django получают доступ и управляют данными через объекты Python, называемые моделями. Модели определяют структуру хранимых данных, включая типы полей и, возможно, их максимальный размер, значения по умолчанию, параметры списка выбора, текст справки для документации, текст меток для форм и т. д. Определение модели не зависит от основной базы данных — вы можете выбрать один из нескольких компонентов вашей настройки проекта. После того, как вы выбрали какую базу данных хотите использовать, вам не нужно напрямую работать с ней — вы просто пишете свою структуру модели и код, а Django делает всю грязную работу, связанную с базой данных за вас.

В этом учебнике показано, как определить и получить доступ к моделям на примере LocalLibrary website.

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

Мы знаем, что нам нужно хранить информацию о книгах (название, резюме, автор, язык, на котором написана книга, категория, ISBN) и что у нас может быть несколько доступных экземпляров (с уникальным глобальным идентификатором, статусом доступности и т. Д.). Нам может потребоваться хранить больше информации об авторе, чем просто их имя, и могут быть несколько авторов с одинаковыми или похожими именами. Мы хотим иметь возможность сортировать информацию на основе названия книги, автора, письменного языка и категории.

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

Вы также можете использовать модели для представления параметров списка выбора (например, как выпадающий список вариантов), вместо жёсткого кодирования выбора на самом веб-сайте — это рекомендуется, когда все варианты неизвестны заранее или могут измениться. Очевидные кандидаты на модели в этом случае включают жанр книги (например, «Научная фантастика», «Французская поэзия» и т. д.) И язык (английский, французский, японский).

Как только мы определились с нашими моделями и полями, нам нужно подумать об отношениях. Django позволяет вам определять отношения, как один к одному (OneToOneField), один ко многим (ForeignKey) и многие ко многим (ManyToManyField).

Диаграмма ассоциации UML, приведённая ниже показывает модели, которые мы определили в этом случае (в виде блоков). Как и выше, мы создали модели для книги (общие сведения о книге), экземпляр книги (статус конкретных физических копий книги, доступных в системе) и автора.Мы также решили создать модель для жанра, чтобы можно было создавать / выбирать значения через интерфейс администратора. Мы решили не иметь модель для BookInstance: status — мы жестко закодировали значения (LOAN_STATUS), потому что мы не ожидаем их изменения. В каждом из полей вы можете увидеть имя модели, имена и типы полей, а также методы и их типы возврата.

На диаграмме также показаны зависимости между моделями, включая их множители. Множители представляют собой числа на диаграмме, показывающие минимум и максимум единиц каждой модели, которые могут присутствовать в этой связи. Например, соединительная линия между ящиками показывает, что книга и жанр связаны между собой. Цифры, близкие к модели жанра, показывают, что у книги может быть один или несколько жанров (сколько угодно), а числа на другом конце строки рядом с моделью книги показывают, что у жанра может быть ноль или более связанных книг.

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

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

Определение модели

Модели обычно определяются в приложении models.py. Они реализуются как подклассы django.db.models.Model, и могут включать поля, методы и метаданные. В приведенном ниже фрагменте кода показана «типичная» модель, названная MyModelName:

from django.db import models

class MyModelName(models.Model):
    """
    A typical class defining a model, derived from the Model class.
    """

    # Fields
    my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")
    ...

    # Metadata
    class Meta:
        ordering = ["-my_field_name"]

    # Methods
    def get_absolute_url(self):
         """
         Returns the url to access a particular instance of MyModelName.
         """
         return reverse('model-detail-view', args=[str(self.id)])

    def __str__(self):
        """
        String for representing the MyModelName object (in Admin site etc. )
        """
        return self.field_name

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

Поля

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

my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")

Наш вышеприведенный пример имеет одно поле, называемое my_field_name, типа models.CharField — что означает, что это поле будет содержать строки буквенно-цифровых символов. Типы полей назначаются с использованием определенных классов, которые определяют тип записи, которая используется для хранения данных в базе данных, а также критерии проверки, которые должны использоваться, когда значения получены из формы HTML (то есть, что составляет действительное значение). Типы полей также могут принимать аргументы, которые дополнительно определяют, как поле хранится или может использоваться. В этом случае мы даем нашему полю два аргумента:

  • max_length=20 — Указывает, что максимальная длина значения в этом поле составляет 20 символов.
  • help_text="Enter field documentation" — предоставляет текстовую метку для отображения, чтобы помочь пользователям узнать, какое значение необходимо предоставить, когда это значение должно быть введено пользователем через HTML-форму.

Имя поля используется для обращения к нему в запросах и шаблонах. В полях также есть метка, которая задается как аргумент (verbose_name), либо выводится путем заглавной буквы первой буквы имени переменной поля и замены любых символов подчеркивания пробелом (например, my_field_name будет иметь метку по умолчанию My field name).

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

Общие аргументы поля

Следующие общие аргументы могут использоваться при объявлении многих / разных типов полей:

  • help_text: Предоставляет текстовую метку для HTML-форм (например, на сайте администратора), как описано выше.
  • verbose_name: Удобо-читаемое имя для поля, используемого в поле метки. Если не указано, Django выведет по умолчанию подробное название от имени поля.
  • default: Значение по умолчанию для поля. Это может быть значение или вызываемый объект, и в этом случае объект будет вызываться каждый раз, когда создается новая запись.
  • null: Если True, Django будет хранить пустые значения как NULL в базе данных для полей, где это уместно (CharField вместо этого сохранит пустую строку). По умолчанию используется значение False.
  • blank: Если True, поле может быть пустым в ваших формах. По умолчанию используется значение False, что означает, что проверка формы Django заставит вас ввести значение. Это часто используется с null = True, потому что если вы хотите разрешить пустые значения, вы также хотите, чтобы база данных могла представлять их соответствующим образом.
  • choices: Группа вариантов для этого поля. Если это предусмотрено, по умолчанию соответствующий виджет формы будет полем выбора с этими вариантами вместо стандартного текстового поля.
  • primary_key: Если True, задает текущее поле в качестве первичного ключа для модели (первичный ключ — это специальный столбец базы данных, предназначенный для однозначной идентификации всех разных записей таблицы). Если в качестве первичного ключа не указано поле, Django автоматически добавит для этой цели поле.

Есть много других вариантов — вы можете просмотреть full list of field options here.

Общие типы полей

Следующие общие аргументы могут использоваться при объявлении многих / разных типов полей:

  • CharField Используется для определения строк фиксированной длины от короткой до средней. Вы должны указать max_length для хранения данных.
  • TextField используется для больших строк произвольной длины. Вы можете указать max_length для поля, но это используется только тогда, когда поле отображается в формах (оно не применяется на уровне базы данных).
  • IntegerField это поле для хранения значений (целого числа) и для проверки введенных значений в виде целых чисел в формах.
  • DateField и DateTimeField используются для хранения / представления дат и информации о дате / времени (как Python datetime.date и datetime.datetime, соответственно). Эти поля могут дополнительно объявлять (взаимоисключающие) параметры auto_now=True (для установки поля на текущую дату каждый раз, когда модель сохраняется), auto_now_add (только для установки даты, когда модель была впервые создана) и по умолчанию (чтобы установить дату по умолчанию, которую пользователь может переустановить).
  • EmailField используется для хранения и проверки адресов электронной почты.
  • FileField и ImageField используются для загрузки файлов и изображений соответственно ( ImageField просто добавляет дополнительную проверку, что загруженный файл является изображением). Они имеют параметры для определения того, как и где хранятся загруженные файлы.
  • AutoField — это особый тип IntegerField, который автоматически увеличивается. Первичный ключ этого типа автоматически добавляется в вашу модель, если вы явно не укажете его.
  • ForeignKey Используется для указания отношения «один ко многим» к другой модели базы данных (например, автомобиль имеет одного производителя, но производитель может делать много автомобилей). «Одна» сторона отношения — это модель, содержащая ключ.
  • ManyToManyField используется для определения отношения «многие ко многим» (например, книга может иметь несколько жанров, и каждый жанр может содержать несколько книг). В нашем приложении для библиотек мы будем использовать их аналогично ForeignKeys, но их можно использовать более сложными способами для описания отношений между группами. Они имеют параметр on_delete, чтобы определить, что происходит, когда связанная запись удаляется (например, значение models.SET_NULL просто установило бы значение NULL)

Существует много других типов полей, включая поля для разных типов чисел (большие целые числа, малые целые числа, дробные), логические значения, URL-адреса, slugs, уникальные идентификаторы и другие «связанные с временем» сведения (продолжительность, время и т. д.). Вы можете просмотреть full list here.

Метаданные

Вы можете объявить метаданные на уровне модели для своей модели, объявив класс Meta, как показано на рисунке.

class Meta:
    ordering = ["-my_field_name"]
    ...

Одной из наиболее полезных функций этих метаданных является управление сотрировкой записей, возвращаемых при запросе типа модели. Вы можете сделать это, указав соответствия названия полей для сортировки, как показано выше. Порядок будет зависеть от типа поля (поля символов отсортированы в алфавитном порядке, а поля даты отсортированы в хронологическом порядке). Как показано выше, вы можете префикс имени поля минус-символом (-), чтобы изменить порядок сортировки.

Например, если мы решили сортировать книги по умолчанию:

ordering = ["title", "-pubdate"]

Книги будут отсортированы по алфавиту по названию, от A-Z, а затем по дате публикации внутри каждого названия, от самого нового до самого старого.

Другим распространенным атрибутом является verbose_name, подробное имя для класса в единственной и множественной форме:

verbose_name = "BetterName"

Другие полезные атрибуты позволяют создавать и применять новые «разрешения доступа» для модели (разрешения по умолчанию применяются автоматически), разрешить упорядочение на основе другого поля или объявить, что класс является «абстрактным» (базовый класс, для которого вы не можете создавать записи, и вместо этого будет создан для создания других моделей). Многие другие параметры метаданных управляют тем, какая база данных должна использоваться для модели и как хранятся данные (это действительно полезно, если вам нужно сопоставить модель с существующей базой данных). Полный список опций метаданных доступен здесь: Model metadata options (Django документация).

Методы

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

def __str__(self):
    return self.field_name

Другим распространенным методом включения в модели Django является get_absolute_url (), который возвращает URL-адрес для отображения отдельных записей модели на веб-сайте (если вы определяете этот метод, тогда Django автоматически добавит кнопку «Просмотр на сайте» на экранах редактирования записей модели на сайте администратора). Типичный шаблон для get_absolute_url () показан ниже.

def get_absolute_url(self):
    """
    Returns the url to access a particular instance of the model.
    """
    return reverse('model-detail-view', args=[str(self.id)])

Примечание. Предполагется, что вы будете использовать URL-адреса, например / myapplication / mymodelname / 2, для отображения отдельных записей для вашей модели (где «2» — это идентификатор для определенной записи), вам нужно будет создать URL-карту, чтобы передать ответ и идентификатор «Образцовое представление модели» (которое будет выполнять работу, необходимую для отображения записи). Вышеуказанная функция reverse () может «перевернуть» ваш URL-адрес (в приведенном выше примере с именем «model-detail-view»), чтобы создать URL-адрес правильного формата.

Конечно, для выполнения этой работы вам все равно придется писать сопоставление URL-адрес, просмотр и шаблон!

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

Управление моделью

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

Создание и изменение записей

Чтобы создать запись, вы можете определить экземпляр модели, а затем вызвать метод save ().


a_record = MyModelName(my_field_name="Instance #1")


a_record. save()

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

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


print(a_record.id) 
print(a_record.my_field_name) 


a_record.my_field_name="New Instance Name"
a_record.save()
Поиск записей

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

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

Мы можем получить все записи для модели как объект QuerySet,  используя objects.all(). QuerySet — это итерируемый объект, означающий, что он содержит несколько объектов, которые мы можем перебирать / прокручивать.

all_books = Book.objects.all()

Метод filter() Django позволяет отфильтровать возвращаемый QuerySet для соответствия указанному текстовому или числовому полю по конкретным критериям. Например, чтобы отфильтровать книги, содержащие  слово «wild» («дикие») в заголовке, а затем подсчитать их, мы могли бы сделать следующее.

wild_books = Book.objects.filter(title__contains='wild')
number_wild_books = Book.objects.filter(title__contains='wild').count()

Соответствующие поля и тип соответствия определяются в имени параметра фильтра, используя формат: field_name__match_type (обратите внимание на двойное подчеркивание между заголовком выше). Выше мы фильтруем заголовок с учетом регистра. Есть много других типов совпадений, которые вы можете сделать: icontains (без учета регистра), iexact (точное совпадение без учета регистра), exact (точное совпадение с учетом регистра ) и in, gt (больше), startswith и т. д смотреть полный список (Django Docs, [EN]).

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

books_containing_genre = Book.objects.filter(genre__name__icontains='fiction')  

Примечание: Вы можете использовать символы подчеркивания (__) для навигации по многим уровням отношений (ForeignKey / ManyToManyField) по своему усмотрению. Например, книга, имеющая разные типы, определяемая с использованием дополнительной связи «обложка», может иметь имя параметра: type__cover__name__exact = ‘hard’.

Существует гораздо больше возможностей для запросов, включая обратные поиски от связанных моделей, цепочки фильтров, возврат меньшего набора значений и т. д. Для получения дополнительной информации см. Making queries (Django Docs, [EN]).

В этом разделе мы начнем определять модели для библиотеки. Откройте models.py (в / locallibrary / catalog /). Шаблон в верхней части страницы импортирует модуль моделей, который содержит базовый класс модели models.Model, от которого наследуются наши модели.

from django.db import models

Модель жанра

Скопируйте приведенный ниже код модели Genre и вставьте его в нижнюю часть вашего файла models.py. Эта модель используется для хранения информации о категории книг — например, будь то художественная или документальная, роман или военно-историческая и т. д. Как уже упоминалось выше, мы создали жанр как модель, а не как свободный текст или список выбора, чтобы возможные значения могли управляться через базу данных, а не были закодированными.

class Genre(models.Model):
    """
    Model representing a book genre (e.g. Science Fiction, Non Fiction).
    """
    name = models.CharField(max_length=200, help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)")

    def __str__(self):
        """
        String for representing the Model object (in Admin site etc.)
        """
        return self.name

Модель имеет один CharField field (имя), которое используется для описания жанра (оно ограничено 200 символами и имеет некоторый help_text. В конце модели мы объявляем метод __str__(), который просто возвращает имя жанра, определенного конкретной записью. Verbose name не был определен, поэтому поле будет называться Name в формах.

Модель книги

Скопируйте модель книги ниже и снова вставьте ее в нижнюю часть файла. Модель книги представляет всю информацию о доступной книге в общем смысле, но не конкретный физический «экземпляр» или «копию» для временного использования. Модель использует CharField для представления названия книги и isbn (обратите внимание, как isbn указывает свой ярлык как «ISBN», используя первый неименованный параметр, поскольку в противном случае ярлык по умолчанию был бы «Isbn»). Модель использует TextField для summary, потому что этот текст, возможно, должен быть очень длинным.

from django.urls import reverse 

class Book(models.Model):
    """
    Model representing a book (but not a specific copy of a book).
    """
    title = models.CharField(max_length=200)
    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
    
    
    summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
    isbn = models.CharField('ISBN',max_length=13, help_text='13 Character <a href="https://www.isbn-international. org/content/what-isbn">ISBN number</a>')
    genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
    
    

    def __str__(self):
        """
        String for representing the Model object.
        """
        return self.title


    def get_absolute_url(self):
        """
        Returns the url to access a particular book instance.
        """
        return reverse('book-detail', args=[str(self.id)])

Жанр представляет из себя ManyToManyField, так что книга может иметь несколько жанров, а жанр может иметь много книг. Автор объявляется через ForeignKey, поэтому в каждой книге будет только один автор, но у автора может быть много книг (на практике книга может иметь несколько авторов, но не в такой реализации!)

В обоих типах полей соответствующий класс модели объявляется как первый неименованный параметр, используя либо класс модели, либо строку, содержащую имя соответствующей модели. Вы должны использовать имя модели как строку, если связанный класс еще не был определен в этом файле до того, как он будет указан! Другими параметрами, представляющими интерес для поля автора, являются null=True, которое позволяет базе данных хранить значение Null , если автор не выбран, и on_delete = models.  SET_NULL установит значение автора в Null, если связанная с автором запись будет удалена.

Модель также определяет __str __ (), используя поле заголовка книги для представления книги. Окончательный метод get_absolute_url () возвращает URL-адрес, который можно использовать для доступа к подробной записи для этой модели (для этого нам нужно будет определить сопоставление URL-адресов, в котором содержится подробная информация о книге, и определить связанное представление и шаблон ).

Модель BookInstance

Затем скопируйте модель BookInstance (показано ниже) под другие модели. BookInstance представляет собой определенную копию книги, которую кто-то может брать взаймы, и включает информацию о том, доступна ли копия или в какой день она ожидается, «отпечаток» или сведения о версии, а также уникальный идентификатор книги в библиотеке. Теперь некоторые из полей и методов будут знакомы. Модель использует

  • ForeignKey для идентификации связанной книги (в каждой книге может быть много копий, но в копии может быть только одна книга).
  • CharField, для представления данных (конкретного выпуска) о книге.
import uuid 

class BookInstance(models.Model):
    """
    Model representing a specific copy of a book (i.e. that can be borrowed from the library).
    """
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="Unique ID for this particular book across whole library")
    book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True)
    imprint = models.CharField(max_length=200)
    due_back = models.DateField(null=True, blank=True)

    LOAN_STATUS = (
        ('m', 'Maintenance'),
        ('o', 'On loan'),
        ('a', 'Available'),
        ('r', 'Reserved'),
    )

    status = models.CharField(max_length=1, choices=LOAN_STATUS, blank=True, default='m', help_text='Book availability')

    class Meta:
        ordering = ["due_back"]


    def __str__(self):
        """
        String for representing the Model object
        """
        return '%s (%s)' % (self. id,self.book.title)

Мы дополнительно объявляем несколько новых типов полей:

  • UUIDField используется для поля id, чтобы установить его как primary_key для этой модели. Этот тип поля выделяет глобальное уникальное значение для каждого экземпляра (по одному для каждой книги, которую вы можете найти в библиотеке).
  • DateField используется для данных due_back (при которых ожидается, что книга появится после заимствования или обслуживания). Это значение может быть blank или null (необходимо, когда книга доступна). Метаданные модели (Class Meta) используют это поле для упорядочивания записей, когда они возвращаются в запросе.
  • status — это CharField, который определяет список choice/selection. Как вы можете видеть, мы определяем кортеж, содержащий кортежи пар ключ-значение и передаем его аргументу выбора. Значение в key/value паре — это отображаемое значение, которое пользователь может выбрать, а ключи — это значения, которые фактически сохраняются, если выбрана опция. Мы также установили значение по умолчанию «m» (техническое обслуживание), поскольку книги изначально будут созданы недоступными до того, как они будут храниться на полках.

Модель __str __ () представляет объект BookInstance, используя комбинацию его уникального идентификатора и связанного с ним заголовка книги.

Примечание. Немного Python’а:

  • Значение, возвращаемое __str __ (), является форматированной строкой. В строке мы используем % S для объявления ‘placeholders’. После строки укажем %, а затем кортеж, содержащий значения, которые будут вставлены в заполнители. Если у вас просто один заполнитель, вы можете опустить кортеж — например, ‘Мое значение:% S’ % переменная.

    Обратите также внимание на то, что, хотя этот подход совершенно применим, но он более не является предпочтительным. Начиная с Python 3, вы должны использовать метод format, например. ‘{0} ({1})’.format (self.id, self.book.title). Вы можете узнать больше об этом  здесь.

Модель автора

Скопируйте модель автора (показано ниже) под существующим кодом в models. py.

Теперь все поля/методы должны быть знакомы. Модель определяет автора как имя, фамилию, дату рождения и (необязательную) дату смерти. Он указывает, что по умолчанию __str __ () возвращает имя в фамилии, порядковый номер первого имени. Метод get_absolute_url () отменяет сопоставление URL-адреса автора с целью получения URL-адреса для отображения отдельного автора.

class Author(models.Model):
    """
    Model representing an author.
    """
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)

    def get_absolute_url(self):
        """
        Returns the url to access a particular author instance.
        """
        return reverse('author-detail', args=[str(self.id)])


    def __str__(self):
        """
        String for representing the Model object.
        """
        return '%s, %s' % (self. last_name, self.first_name)

Теперь все ваши модели созданы. Теперь переустановите миграцию базы данных, чтобы добавить их в свою базу данных.

python3 manage.py makemigrations
python3 manage.py migrate

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

Некоторые вещи, которые следует учитывать:

  • Должен ли «язык» ассоциироваться с Book, BookInstance или каким-либо другим объектом?
  • Должны ли быть представлены разные языки с использованием модели, свободного текстового поля или жестко запрограммированного списка выбора?

После того, как вы решили, добавьте поле. Вы можете увидеть наше решение на Github here.

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

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

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

Этот документ содержит весь справочник по API Field, включая field options и field types, предлагаемые Django.

Опции полей¶

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

null


Field.null

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

Избегайте использования null в строковых полях, таких как CharField и TextField. Если строковое поле имеет значение null=True, это означает, что у него есть два возможных значения для «нет данных»: NULL и пустая строка. В большинстве случаев избыточно иметь два возможных значения для «нет данных»; соглашение Django заключается в использовании пустой строки, а не NULL. Единственное исключение — когда a CharField имеет оба значения: unique=True и blank=True. В этой ситуации требуется null=True, чтобы избежать нарушений ограничений при сохранении нескольких объектов с пустыми значениями.

Как для строковых, так и для нестроковых полей вам также нужно установить blank=True, если вы хотите разрешить пустые значения в формах, поскольку параметр null влияет только на хранилище базы данных (см. blank).

Примечание

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

blank


Field.blank

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

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

choices


Field.choices

A sequence consisting itself of iterables of exactly two items (e.g.
[(A, B), (A, B) ...]) to use as choices for this field. If choices are
given, they’re enforced by model validation and the
default form widget will be a select box with these choices instead of the
standard text field.

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

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

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

from django. db import models

class Student(models.Model):
    FRESHMAN = 'FR'
    SOPHOMORE = 'SO'
    JUNIOR = 'JR'
    SENIOR = 'SR'
    GRADUATE = 'GR'
    YEAR_IN_SCHOOL_CHOICES = [
        (FRESHMAN, 'Freshman'),
        (SOPHOMORE, 'Sophomore'),
        (JUNIOR, 'Junior'),
        (SENIOR, 'Senior'),
        (GRADUATE, 'Graduate'),
    ]
    year_in_school = models.CharField(
        max_length=2,
        choices=YEAR_IN_SCHOOL_CHOICES,
        default=FRESHMAN,
    )

    def is_upperclass(self):
        return self.year_in_school in {self.JUNIOR, self.SENIOR}

Хотя вы можете определить список вариантов за пределами класса модели и затем обратиться к нему, определение вариантов и имен для каждого варианта внутри класса модели сохраняет всю эту информацию вместе с классом, который его использует, и помогает ссылаться на варианты (например, « Student.SOPHOMORE« будет работать везде, где была импортирована модель « Student«).

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

MEDIA_CHOICES = [
    ('Audio', (
            ('vinyl', 'Vinyl'),
            ('cd', 'CD'),
        )
    ),
    ('Video', (
            ('vhs', 'VHS Tape'),
            ('dvd', 'DVD'),
        )
    ),
    ('unknown', 'Unknown'),
]

The first element in each tuple is the name to apply to the group. The
second element is an iterable of 2-tuples, with each 2-tuple containing
a value and a human-readable name for an option. Grouped options may be
combined with ungrouped options within a single list (such as the
'unknown' option in this example).

Для каждого поля модели, для которого установлено choices, Django добавит метод для извлечения понятного человеку имени для текущего значения поля. Смотрите get_FOO_display() в документации по API базы данных.

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

Примечание

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

Если в поле не установлено blank=False вместе с default, то метка, содержащая "---------" будет отображаться с полем выбора. Чтобы переопределить это поведение, добавьте кортеж в choices, содержащий None; например (None, 'Your String For Display'). В качестве альтернативы вы можете использовать пустую строку вместо None, где это имеет смысл — например, для CharField.

Типы перечисления¶

Кроме того, Django предоставляет типы перечисления, которые вы можете создать на подклассе для краткого определения вариантов:

from django.utils.translation import gettext_lazy as _

class Student(models.Model):

    class YearInSchool(models.TextChoices):
        FRESHMAN = 'FR', _('Freshman')
        SOPHOMORE = 'SO', _('Sophomore')
        JUNIOR = 'JR', _('Junior')
        SENIOR = 'SR', _('Senior')
        GRADUATE = 'GR', _('Graduate')

    year_in_school = models.CharField(
        max_length=2,
        choices=YearInSchool.choices,
        default=YearInSchool.FRESHMAN,
    )

    def is_upperclass(self):
        return self.year_in_school in {
            self.YearInSchool.JUNIOR,
            self.YearInSchool.SENIOR,
        }

Они работают аналогично enum из стандартной библиотеки Python, но с некоторыми изменениями:

  • Значения членов перечисления являются набором аргументов для использования при построении конкретного типа данных. Django поддерживает добавление дополнительного строкового значения в конец этого кортежа для использования в качестве понятного человеку имени или label. label может быть ленивой (lazy) переводимой строкой. Таким образом, в большинстве случаев значением члена будет кортеж из вдух значений (value, label). Смотрите ниже пример выбора подкласса с использованием более сложного типа данных. Если кортеж не указан или последний элемент не является (ленивой) строкой, то label автоматически генерируется из имени члена.
  • Свойство .label добавляется в значения, чтобы вернуть удобочитаемое имя.
  • Ряд пользовательских свойств добавлен в классы перечисления – .choices, .labels, .values и .names – чтобы упростить доступ к спискам из этих отдельных частей перечисления. Используйте .choices в качестве подходящего значения для передачи choices в определении поля.
  • Использование enum.unique() применяется для обеспечения того, чтобы значения не могли быть определены несколько раз. Этого вряд ли можно ожидать при выборе поля.

Обратите внимание, что использование YearInSchool.SENIOR, YearInSchool['SENIOR'] или YearInSchool('SR') для доступа к элементам перечисления или их поиска работает должным образом, как и .name и .value свойства для членов.

Если вам не нужно переводить понятные человеку имена, вы можете сделать их выводимыми из имени члена (заменив подчеркивание пробелами и используя title-case):

>>> class Vehicle(models.TextChoices):
...     CAR = 'C'
...     TRUCK = 'T'
...     JET_SKI = 'J'
...
>>> Vehicle.JET_SKI.label
'Jet Ski'

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

class Card(models.Model):

    class Suit(models.IntegerChoices):
        DIAMOND = 1
        SPADE = 2
        HEART = 3
        CLUB = 4

    suit = models.IntegerField(choices=Suit.choices)

Также можно использовать Enum Functional API с предупреждением о том, что метки генерируются автоматически, как указано выше:

>>> MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
>>> MedalType.choices
[('GOLD', 'Gold'), ('SILVER', 'Silver'), ('BRONZE', 'Bronze')]
>>> Place = models.IntegerChoices('Place', 'FIRST SECOND THIRD')
>>> Place.choices
[(1, 'First'), (2, 'Second'), (3, 'Third')]

Если вам требуется поддержка конкретного типа данных, отличного от int или str, вы можете создать подкласс Choices и требуемый конкретный тип данных, например, date для использования с DateField:

class MoonLandings(datetime. date, models.Choices):
    APOLLO_11 = 1969, 7, 20, 'Apollo 11 (Eagle)'
    APOLLO_12 = 1969, 11, 19, 'Apollo 12 (Intrepid)'
    APOLLO_14 = 1971, 2, 5, 'Apollo 14 (Antares)'
    APOLLO_15 = 1971, 7, 30, 'Apollo 15 (Falcon)'
    APOLLO_16 = 1972, 4, 21, 'Apollo 16 (Orion)'
    APOLLO_17 = 1972, 12, 11, 'Apollo 17 (Challenger)'

Есть несколько дополнительных предостережений, о которых следует знать:

  • Типы перечисления не поддерживают именованные группы.

  • Поскольку перечисление с конкретным типом данных требует, чтобы все значения соответствовали типу, переопределение пустой метки не может быть достигнуто путем создания элемента со значением None , вместо этого установите атрибут __empty__ в классе:

    class Answer(models.IntegerChoices):
        NO = 0, _('No')
        YES = 1, _('Yes')
    
        __empty__ = _('(Unknown)')
    

New in Django 3. 0:

Были добавлены классы TextChoices, IntegerChoices и Choices.

db_column


Field.db_column

Имя столбца базы данных для использования в этом поле. Если это не указано, Django будет использовать имя поля.

Если имя столбца вашей базы данных является зарезервированным словом SQL или содержит символы, которые не допускаются в именах переменных Python, особенно дефис, то это нормально. За кулисами Джанго цитирует имена столбцов и таблиц.

db_index


Field.db_index

Если True, для этого поля будет создан индекс базы данных.

db_tablespace


Field.db_tablespace

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

default


Field.default

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

Значением по умолчанию не может быть изменяемый объект (экземпляр модели, list, set и т.п.), Поскольку ссылка на тот же экземпляр этого объекта будет использоваться в качестве значения по умолчанию во всех новых моделях. экземпляров. Вместо этого оберните желаемое значение по умолчанию в вызываемый. Например, если вы хотите указать по умолчанию dict для JSONField, используйте функцию:

def contact_default():
    return {"email": "to1@example. com"}

contact_info = JSONField("ContactInfo", default=contact_default)

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

Для таких полей, как ForeignKey, которые отображаются на экземпляры модели, значения по умолчанию должны быть значением поля, на которое они ссылаются (pk, если не установлено to_field), а не экземплярами модели.

Значение по умолчанию используется, когда создаются новые экземпляры модели, а значение для поля не указывается. Когда поле является первичным ключом, значение по умолчанию также используется, когда поле установлено в None.

editable


Field.editable

Если False, поле не будет отображаться ни админкой, ни какими-либо другими ModelForm. Они также пропускаются во время проверки модели. По умолчанию установлено значение True.

error_messages


Field.error_messages

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

Ключи сообщений об ошибке включают null, blank, invalid, invalid_choice, unique и unique_for_date. Дополнительные ключи сообщений об ошибках указаны для каждого поля в разделе «Типы полей» Field types ниже.

Эти сообщения об ошибках часто не распространяются на формы. Смотрите Considerations regarding model’s error_messages.

help_text


Field.help_text

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

Обратите внимание, что это значение не экранировано HTML в автоматически сгенерированных формах. Это позволяет вам включать HTML в help_text, если вы того пожелаете. Например:

help_text="Please use the following format: <em>YYYY-MM-DD</em>."

В качестве альтернативы вы можете использовать обычный текст и django.utils.html.escape() для экранирования любых специальных символов HTML. Убедитесь, что вы избегаете любого текста справки, который может прийти от ненадежных пользователей, чтобы избежать атаки межсайтового скриптинга.

primary_key


Field.primary_key

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

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

primary_key=True подразумевает null=False и unique=True. На объекте разрешен только один первичный ключ.

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

unique


Field.unique

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

Это обеспечивается на уровне базы данных и проверкой модели. Если вы попытаетесь сохранить модель с повторяющимся значением в поле unique, то будет вызвано исключение django.db.IntegrityError методом save().

Эта опция действительна для всех типов полей, кроме ManyToManyField и OneToOneField.

Обратите внимание, что когда unique равно True, вам не нужно указывать db_index, потому что unique подразумевает создание индекса.

unique_for_date


Field.unique_for_date

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

Например, если у вас есть поле title, которое имеет unique_for_date="pub_date" ``, то Django не разрешит ввод двух записей с одинаковыми ``title и pub_date.

Обратите внимание, что если вы установите это значение для DateTimeField, будет учитываться только часть поля — дата. Кроме того, когда USE_TZ равно True, проверка будет выполняться в текущий часовой пояс во время сохранения объекта.

Это обеспечивается методом Model.validate_unique() во время проверки модели, но не на уровне базы данных. Если ограничение unique_for_date включает поля, которые не являются частью ModelForm (например, если одно из полей указано в exclude или имеет editable=False), Model. validate_unique() пропустит проверку для этого конкретного ограничения.

unique_for_month


Field.unique_for_month

Как unique_for_date, но требует, чтобы поле было уникальным по отношению к месяцу.

verbose_name


Field.verbose_name

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

validators


Field.validators

Список валидаторов для этого поля. См. Документацию валидаторы для получения дополнительной информации.

Регистрация и выборка поисков¶

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

Типы полей¶

AutoField


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

IntegerField, который автоматически увеличивается в соответствии с доступными идентификаторами. Вам обычно не нужно использовать это напрямую; поле первичного ключа будет автоматически добавлено в вашу модель, если вы не укажете иное. Смотрите Автоматические поля первичного ключа.

BigAutoField


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

64-разрядное целое число, очень похожее на AutoField, за исключением того, что оно гарантированно соответствует числам от 1 до 9223372036854775807.

BigIntegerField


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

A 64-bit integer, much like an IntegerField except that it is
guaranteed to fit numbers from -9223372036854775808 to
9223372036854775807. The default form widget for this field is a
NumberInput.

BinaryField


class BinaryField(max_length=None, **options)[исходный код]¶

Поле для хранения необработанных двоичных данных. Может быть назначено bytes, bytearray или memoryview.

По умолчанию BinaryField устанавливает editable в False, и в этом случае он не может быть включен в ModelForm.

BinaryField имеет один дополнительный необязательный аргумент:


BinaryField.max_length

Максимальная длина (в символах) поля. Максимальная длина применяется в проверке Django с использованием MaxLengthValidator.

Злоупотребление BinaryField

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

CharField


class CharField(max_length=None, **options)[исходный код]¶

Строковое поле, для строк малого и большого размера.

Для больших объемов текста используйте TextField.

Виджет формы по умолчанию для этого поля TextInput.

CharField имеет один дополнительный обязательный аргумент:


CharField.max_length

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

Примечание

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

DateField


class DateField(auto_now=False, auto_now_add=False, **options)[исходный код]¶

Дата, представленная в Python экземпляром datetime.date. Имеет несколько дополнительных необязательных аргументов:


DateField.auto_now

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

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


DateField. auto_now_add

Автоматически установить поле на сейчас, когда объект создается впервые. Полезно для создания меток времени. Обратите внимание, что текущая дата всегда используется; это не просто значение по умолчанию, которое вы можете переопределить. Так что даже если вы установите значение для этого поля при создании объекта, оно будет проигнорировано. Если вы хотите изменить это поле, установите вместо auto_now_add=True следующее:

The default form widget for this field is a
DateInput. The admin adds a JavaScript calendar,
and a shortcut for «Today». Includes an additional invalid_date error
message key.

Опции auto_now_add, auto_now и default являются взаимоисключающими. Любая комбинация этих параметров приведет к ошибке.

Примечание

Как в настоящее время реализовано, установка auto_now или auto_now_add в True приведет к тому, что в поле будут установлены editable=False и blank=True.

Примечание

Опции auto_now и auto_now_add всегда будут использовать дату в часовом поясе по умолчанию в момент создания или обновления. Если вам нужно что-то другое, вы можете рассмотреть возможность использования собственной функции вызываемой по умолчанию или переопределения save() вместо использования auto_now или auto_now_add; или используя DateTimeField вместо « DateField« и решая, как обрабатывать преобразование из даты-времени в дату во время отображения.

DateTimeField


class DateTimeField(auto_now=False, auto_now_add=False, **options)[исходный код]¶

Дата и время, представленные в Python экземпляром datetime.datetime. Принимает те же дополнительные аргументы, что и DateField.

The default form widget for this field is a single
DateTimeInput. The admin uses two separate
TextInput widgets with JavaScript shortcuts.

DecimalField


class DecimalField(max_digits=None, decimal_places=None, **options)[исходный код]¶

Десятичное число с фиксированной точностью, представленное в Python экземпляром Decimal. Он проверяет ввод с помощью DecimalValidator.

Имеет два обязательных аргумента:


DecimalField.max_digits

Максимально допустимое количество цифр в числе. Обратите внимание, что это число должно быть больше или равно decimal_places.


DecimalField.decimal_places

Количество десятичных разрядов для хранения с числом.

Например, чтобы хранить числа до 999 с разрешением в 2 десятичных знака, вы должны использовать:

models. DecimalField(..., max_digits=5, decimal_places=2)

И хранить цифры примерно до миллиарда с разрешением 10 десятичных знаков:

models.DecimalField(..., max_digits=19, decimal_places=10)

Виджетом формы по умолчанию для этого поля является NumberInput, когда localize False или TextInput в противном случае.

DurationField


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

Поле для хранения периодов времени — смоделировано в Python с помощью timedelta. При использовании в PostgreSQL используемый тип данных представляет собой interval, а в Oracle тип данных представляет собой INTERVAL DAY (9) TO SECOND (6). В противном случае используется bigint микросекунд.

Примечание

Арифметика с DurationField работает в большинстве случаев. Однако во всех базах данных, кроме PostgreSQL, сравнение значения DurationField с арифметикой в экземплярах DateTimeField не будет работать должным образом.

EmailField


class EmailField(max_length=254, **options)[исходный код]¶

Класс CharField, который проверяет, является ли значение действительным адресом электронной почты, используя EmailValidator.

FileField


class FileField(upload_to=None, max_length=100, **options)[исходный код]¶

Поле для загрузки файла.

Примечание

Аргумент primary_key не поддерживается и выдает ошибку, если используется.

Имеет два необязательных аргумента:


FileField.upload_to

Этот атрибут обеспечивает способ указания каталога загрузки и имени файла и может быть установлен двумя способами. В обоих случаях значение передается методу Storage.save().

Если указать строковое значение или Path, оно может содержать форматирование strftime(), которое будет заменено датой/временем загрузки файла (чтобы загруженные файлы не заполняли заданный каталог). Например:

class MyModel(models.Model):
    # file will be uploaded to MEDIA_ROOT/uploads
    upload = models.FileField(upload_to='uploads/')
    # or...
    # file will be saved to MEDIA_ROOT/uploads/2015/01/30
    upload = models.FileField(upload_to='uploads/%Y/%m/%d/')

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

upload_to также может быть вызываемым объектом, например, функцией. Он будет вызван для получения пути загрузки, включая имя файла. Этот вызываемый объект должен принимать два аргумента и возвращать путь в стиле Unix (с косой чертой) для передачи в систему хранения. Два аргумента:




АргументОписание
instance

Экземпляр модели, где определено FileField. Более конкретно, это случай, когда текущий файл присоединяется.

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

filenameИмя файла, которое изначально было указано в файле. Это может или не может быть принято во внимание при определении окончательного пути назначения.

Например:

def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'user_{0}/{1}'.format(instance. user.id, filename)

class MyModel(models.Model):
    upload = models.FileField(upload_to=user_directory_path)

Changed in Django 3.0:

Добавлена поддержка pathlib.Path.


FileField.storage

Объект хранения, который управляет хранением и поиском ваших файлов. Смотрите Managing files для получения подробной информации о том, как предоставить этот объект.

Виджетом формы по умолчанию для этого поля является ClearableFileInput.

Использование FileField или ImageField (см. ниже) в модели требует нескольких шагов:

  1. В вашем файле настроек вам нужно определить MEDIA_ROOT в качестве полного пути к каталогу, в котором вы хотите, чтобы Django сохранял загруженные файлы. (Для производительности эти файлы не хранятся в базе данных.) Определите MEDIA_URL в качестве базового общедоступного URL-адреса этого каталога. Убедитесь, что этот каталог доступен для записи учетной записи веб-сервера.
  2. Добавьте FileField или ImageField к вашей модели, определив параметр upload_to, чтобы указать подкаталог MEDIA_ROOT, который будет использоваться для загружаемых файлов.
  3. Все, что будет храниться в вашей базе данных, это путь к файлу (относительно MEDIA_ROOT). Вы, скорее всего, захотите использовать url, предоставленный Django. Например, если ваш ImageField называется mug_shot, вы можете получить абсолютный путь к вашему изображению в шаблоне с помощью {{ object.mug_shot.url }}.

Например, скажем, что ваш параметр MEDIA_ROOT установлен в '/home/media', а для upload_to установлен 'photos/%Y/%m/%d'. Часть '%Y/%m/%d' в upload_to имеет форматирование strftime(); '%Y' — четырехзначный год, '%m' — двузначный месяц, а '% d' — двузначный день. Если вы загрузите файл 15 января 2007 года, он будет сохранен в каталоге /home/media/photos/2007/01/15.

Если вы хотите получить имя файла загруженного файла на диске или размер файла, вы можете использовать атрибуты name и File соответственно; для получения дополнительной информации о доступных атрибутах и методах см. File и руководство по темам Managing files.

Примечание

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

Относительный URL загруженного файла можно получить с помощью атрибута url. Внутренне это вызывает метод url() базового класса Storage.

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

Также обратите внимание, что даже загруженный файл HTML, поскольку он может выполняться браузером (но не сервером), может создавать угрозы безопасности, эквивалентные атакам XSS или CSRF.

Экземпляры FileField создаются в вашей базе данных как varchar столбцы с максимальной длиной по умолчанию 100 символов. Как и в других полях, вы можете изменить максимальную длину, используя аргумент max_length.

FileField и FieldFile


class FieldFile[исходный код]¶

Когда вы обращаетесь к FileField в модели, вам предоставляется экземпляр FieldFile в качестве прокси для доступа к базовому файлу.

API класса FieldFile отражает интерфейс класса File, с одним ключевым отличием: Объект, обернутый классом, не обязательно является оберткой вокруг встроенного в Python файлового объекта. Вместо этого он является оберткой вокруг результата метода Storage.open(), который может быть File, или это может быть реализация пользовательского хранилища API File.

В дополнение к API, унаследованному от File, такого как read()``и ``write(), FieldFile включает в себя несколько методов, которые могут использоваться для взаимодействия с базовым файлом:

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

Два метода этого класса save() и delete(), по умолчанию сохраняют объект модели связанного FieldFile в базе данных.


FieldFile.name

Имя файла, включая относительный путь от корня Storage связанного FileField.


FieldFile.size

Результат базового метода Storage.size().


FieldFile.url

Доступное только для чтения свойство для доступа к относительному URL-адресу файла путем вызова метода url() базового класса Storage.


FieldFile.open(mode=’rb’)[исходный код]¶

Открывает или повторно открывает файл, связанный с этим экземпляром, в указанном режиме mode. В отличие от стандартного метода open() Python, он не возвращает файловый дескриптор.

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


FieldFile.close()[исходный код]¶

Ведет себя как стандартный метод file.close() Python и закрывает файл, связанный с этим экземпляром.


FieldFile.save(name, content, save=True)[исходный код]¶

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

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

Обратите внимание, что аргумент content должен быть экземпляром django.core.files.File, а не встроенным файловым объектом Python. Вы можете создать File из существующего объекта файла Python, например:

from django.core.files import File
# Open an existing file using Python's built-in open()
f = open('/path/to/hello.world')
myfile = File(f)

Или вы можете построить один из строки Python, как здесь:

from django.core.files.base import ContentFile
myfile = ContentFile("hello world")

Для получения дополнительной информации смотрите Managing files.


FieldFile.delete(save=True)[исходный код]¶

Удаляет файл, связанный с этим экземпляром, и очищает все атрибуты в поле. Примечание. Этот метод закрывает файл, если он открывается при вызове delete().

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

Обратите внимание, что при удалении модели связанные файлы не удаляются. Если вам нужно очистить потерянные файлы, вам придется обрабатывать их самостоятельно (например, с помощью настраиваемой команды управления, которую можно запускать вручную или запускать по расписанию, например, через cron).

FilePathField


class FilePathField(path=», match=None, recursive=False, allow_files=True, allow_folders=False, max_length=100, **options)[исходный код]¶

A CharField whose choices are limited to the filenames in a certain
directory on the filesystem. Has some special arguments, of which the first is
required:


FilePathField.path

Необходимые. Абсолютный путь файловой системы к каталогу, из которого FilePathField должен получить свой выбор. Пример: "/home/images".

path также может вызываться, например, функция для динамического задания пути во время выполнения. Пример:

import os
from django.conf import settings
from django.db import models

def images_path():
    return os.path.join(settings.LOCAL_FILE_DIR, 'images')

class MyModel(models.Model):
    file = models.FilePathField(path=images_path)

Changed in Django 3.0:

path теперь может быть вызываемым.


FilePathField.match

По желанию. Регулярное выражение в виде строки, которое FilePathField будет использовать для фильтрации имен файлов. Обратите внимание, что регулярное выражение будет применяться к базовому имени файла, а не к полному пути. Пример: "foo.*\.txt$", который будет соответствовать файлу с именем foo23.txt, но не bar.txt или foo23.png.


FilePathField.recursive

По желанию. Либо True, либо False. По умолчанию установлено значение False. Указывает, должны ли быть включены все подкаталоги path


FilePathField.allow_files

По желанию. Либо True, либо False. По умолчанию установлено значение True. Указывает, следует ли включать файлы в указанном месте. Либо это, либо allow_folders должно быть True.


FilePathField.allow_folders

По желанию. Либо True, либо False. По умолчанию установлено значение False. Указывает, следует ли включать папки в указанном месте. Либо это, либо allow_files должно быть True.

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

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

FilePathField(path="/home/images", match="foo.*", recursive=True)

…будет соответствовать /home/images/foo.png, но не /home/images/foo/bar.png, потому что match применяется к базовому имени файла (foo.png и bar.png).

Экземпляры FilePathField создаются в вашей базе данных как varchar столбцы с максимальной длиной по умолчанию 100 символов. Как и в других полях, вы можете изменить максимальную длину, используя аргумент max_length.

FloatField


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

Число с плавающей точкой, представленное в Python экземпляром float.

Виджетом формы по умолчанию для этого поля является NumberInput, когда localize False или TextInput в противном случае.

FloatField или DecimalField

Класс FloatField иногда сопоставляют с классом DecimalField. Хотя они оба представляют действительные числа, они представляют эти числа по-разному. FloatField использует внутри Python тип float, а DecimalField использует тип Decimal. Информацию о разнице между ними смотрите в документации Python для модуля decimal.

ImageField


class ImageField(upload_to=None, height_field=None, width_field=None, max_length=100, **options)[исходный код]¶

Наследует все атрибуты и методы из FileField, но также проверяет, что загруженный объект является допустимым изображением.

В дополнение к специальным атрибутам, которые доступны для FileField, ImageField также имеет атрибуты height и width.

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


ImageField.height_field

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


ImageField.width_field

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

Требуется библиотека Pillow.

Экземпляры ImageField создаются в вашей базе данных как varchar столбцы с максимальной длиной по умолчанию 100 символов. Как и в других полях, вы можете изменить максимальную длину, используя аргумент max_length.

Виджетом формы по умолчанию для этого поля является ClearableFileInput.

IntegerField


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

Целое число. Значения от -2147483648 до 2147483647 безопасны во всех базах данных, поддерживаемых Django.

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

Виджетом формы по умолчанию для этого поля является NumberInput, когда localize False или TextInput в противном случае.

GenericIPAddressField


class GenericIPAddressField(protocol=’both’, unpack_ipv4=False, **options)[исходный код]¶

Адрес IPv4 или IPv6 в строковом формате (например, 192.0.2.30 или 2a02:42fe::4). Виджет формы по умолчанию для этого поля TextInput.

Ниже приведена нормализация адреса IPv6 RFC 4291#section-2.2 раздел 2.2, включая использование формата IPv4, предложенного в параграфе 3 этого раздела, например ::ffff:192.0.2.0. Например, 2001:0: 0:01 будет нормализовано до 2001::1, а ::ffff:0a0a:0a0a до ::ffff:10.10.10.10. Все символы конвертируются в нижний регистр.


GenericIPAddressField.protocol

Ограничивает допустимый ввод указанным протоколом. Допустимые значения: 'both' (по умолчанию), 'IPv4' или 'IPv6'. Соответствие регистронезависимо.


GenericIPAddressField.unpack_ipv4

Ограничивает допустимый ввод указанным протоколом. Допустимые значения: 'both' (по умолчанию), 'IPv4' или 'IPv6'. Соответствие регистронезависимо….

Если вы разрешаете пустые значения, вы должны разрешить нулевые значения, так как пустые значения сохраняются как нулевые.

NullBooleanField


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

Например BooleanField с null=True. Используйте это вместо этого поля, так как оно может быть устаревшим в будущей версии Django.

PositiveIntegerField


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

Подобно IntegerField, но должно быть либо положительным, либо нулевым (0). Значения от 0 до 2147483647 безопасны во всех базах данных, поддерживаемых Django. Значение 0 принимается по причинам обратной совместимости.

PositiveSmallIntegerField


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

Как и PositiveIntegerField, но допускает значения только в определенной (зависящей от базы данных) точке. Значения от 0 до 32767 безопасны во всех базах данных, поддерживаемых Django.

SlugField


class SlugField(max_length=50, **options)[исходный код]¶

Slug is a newspaper term. A slug is a short label for something,
containing only letters, numbers, underscores or hyphens. They’re generally used
in URLs.

Как и в случае CharField, вы можете указать max_length (читайте заметку о переносимости базы данных и max_length в этом разделе). Если max_length не указан, Django будет использовать длину по умолчанию 50.

Подразумевает установку Field.db_index в True.

Часто полезно автоматически предварительно заполнить поле SlugField на основе значения некоторого другого значения. Вы можете сделать это автоматически в админке, используя prepopulated_fields.

Он использует validate_slug или validate_unicode_slug для проверки.


SlugField.allow_unicode

Если True, поле принимает символы Unicode в дополнение к символам ASCII. По умолчанию False.

SmallAutoField


class SmallAutoField(**options


New in Django 3.0.

Подобно AutoField, но допускает только значения в определенном (зависящим от базы данных) диапазоном. Значения от 1 до 32767 безопасны во всех базах данных, поддерживаемых Django.

SmallIntegerField


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

Подобно IntegerField, но допускает значения только в определенной (зависящей от базы данных) точке. Значения от -32768 до 32767 безопасны во всех базах данных, поддерживаемых Django.

TextField


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

Большое текстовое поле. Виджет формы по умолчанию для этого поля Textarea.

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

TimeField


class TimeField(auto_now=False, auto_now_add=False, **options)[исходный код]¶

Время, представленное в Python экземпляром datetime.time. Принимает те же параметры автоматического заполнения, что и DateField.

The default form widget for this field is a TimeInput.
The admin adds some JavaScript shortcuts.

UUIDField


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

Поле для хранения универсально уникальных идентификаторов. Использует класс Python UUID. При использовании в PostgreSQL сохраняется в типе данных uuid, иначе в char(32).

Универсально уникальные идентификаторы являются хорошей альтернативой AutoField для primary_key. База данных не будет генерировать UUID для вас, поэтому рекомендуется использовать default:

import uuid
from django.db import models

class MyUUIDModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # other fields

Обратите внимание, что вызываемый (без круглых скобок) передается в default, а не в экземпляр UUID.

Поиск в PostgreSQL

Использование iexact, contains, icontains, startswith, istartswith, endswith, или iendswith поиска в PostgreSQL не работает для значений без дефисов, потому что PostgreSQL хранит их в типе переноса с типом uuid.

Поля отношений¶

Джанго также определяет набор полей, которые представляют отношения.

ForeignKey


class ForeignKey(to, on_delete, **options)[исходный код]¶

Отношения многие-к-одному. Требуются два позиционных аргумента: класс, к которому относится модель, и опция on_delete.

Чтобы создать рекурсивное отношение — объект, который имеет отношение «многие-к-одному» с самим собой — используйте models.ForeignKey('self', on_delete = models.CASCADE).

Если вам нужно создать отношение на модель, которая еще не была определена, вы можете использовать имя модели, а не сам объект модели:

from django.db import models

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

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

Отношения, определенные следующим образом абстрактные модели, разрешаются, когда модель подклассифицируется как конкретная модель, и не относятся к app_label абстрактной модели:

from django.db import models

class AbstractCar(models.Model):
    manufacturer = models.ForeignKey('Manufacturer', on_delete=models.CASCADE)

    class Meta:
        abstract = True
from django.db import models
from products.models import AbstractCar

class Manufacturer(models.Model):
    pass

class Car(AbstractCar):
    pass

# Car.manufacturer will point to `production.Manufacturer` here.

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

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

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

Индекс базы данных автоматически создается для ForeignKey. Вы можете отключить это, установив db_index в False. Возможно, вы захотите избежать накладных расходов на индекс, если вы создаете внешний ключ для согласованности, а не для объединений, или если вы будете создавать альтернативный индекс, такой как индекс с частичным или множественным столбцом.

Использование в базе данных¶

За кулисами Django добавляет "_id" к имени поля, чтобы создать имя столбца базы данных. В приведенном выше примере таблица базы данных для модели Car будет иметь столбец factory_id. (Вы можете изменить это явно, указав db_column). Однако ваш код никогда не должен иметь дело с именем столбца базы данных, если вы не пишете пользовательский SQL. Вы всегда будете иметь дело с именами полей вашего модельного объекта.

Аргументы¶

ForeignKey принимает другие аргументы, которые определяют детали работы отношений.


ForeignKey.on_delete

При удалении объекта, на который ссылается ForeignKey, Django будет эмулировать поведение ограничения SQL, заданного аргументом on_delete. Например, если у вас есть обнуляемым ForeignKey и вы хотите, чтобы он был установлен в null, когда ссылочный объект удален:

user = models.ForeignKey(
    User,
    models.SET_NULL,
    blank=True,
    null=True,
)

on_delete doesn’t create an SQL constraint in the database. Support for
database-level cascade options may be implemented later.

Возможные значения для on_delete находятся в django.db.models:


  • CASCADE[исходный код]¶

    Каскадное удаление. Django эмулирует поведение ограничения SQL ON DELETE CASCADE, а также удаляет объект, содержащий ForeignKey.

    Model.delete() не вызывается в связанных моделях, но сигналы pre_delete и post_delete отправляются для всех удаленных объектов.


  • PROTECT[исходный код]¶

    Предотвращает удаление объекта, на который есть ссылка, путем вызова ProtectedError, подкласса django.db.IntegrityError.


  • SET_NULL[исходный код]¶

    Устанавливает ForeignKey null; это возможно только в том случае, если null равно True.


  • SET_DEFAULT[исходный код]¶

    Устанавливает для ForeignKey значение по умолчанию; значение по умолчанию для ForeignKey должно быть указано в описании поля.


  • SET()[исходный код]¶

    Устанавливает для ForeignKey значение, переданное в SET(), или, если передан вызываемый объект, результат его вызова. В большинстве случаев передача вызываемого будет необходима, чтобы избежать выполнения запросов во время импорта вашего models.py:

    from django.conf import settings
    from django.contrib.auth import get_user_model
    from django.db import models
    
    def get_sentinel_user():
        return get_user_model().objects.get_or_create(username='deleted')[0]
    
    class MyModel(models.Model):
        user = models.ForeignKey(
            settings.AUTH_USER_MODEL,
            on_delete=models.SET(get_sentinel_user),
        )
    


  • DO_NOTHING[исходный код]¶

    Не предпринимает никаких действий. Если ваша база данных обеспечивает ссылочную целостность, это вызовет IntegrityError, если вы вручную не добавите ограничение SQL ON DELETE в поле базы данных.


ForeignKey.limit_choices_to

Устанавливает ограничение на доступные варианты выбора для этого поля, когда это поле отображается с помощью ModelForm или в админке (по умолчанию все объекты в наборе запросов доступны для выбора). Можно использовать словарь, объект Q или вызываемый объект, возвращающий словарь или объект Q.

Например:

staff_member = models.ForeignKey(
    User,
    on_delete=models.CASCADE,
    limit_choices_to={'is_staff': True},
)

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

Вызываемая форма может быть полезна, например, при использовании совместно с модулем Python datetime для ограничения выбора по диапазону дат. Например:

def limit_pub_date_choices():
    return {'pub_date__lte': datetime.date.utcnow()}

limit_choices_to = limit_pub_date_choices

Если limit_choices_to равно или возвращает Q объект, который полезен для сложных запросов, тогда он будет влиять только на варианты, доступные в админке, если поле не указано в raw_id_fields в ModelAdmin для модели.

Примечание

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


ForeignKey.related_name

Имя, используемое для отношения от связанного объекта обратно к нему. Это также значение по умолчанию для related_query_name (имя, используемое для имени обратного фильтра из целевой модели). Смотрите документацию по связанным объектам для полного объяснения и примера. Обратите внимание, что вы должны установить это значение при определении отношений абстрактных моделей; и когда вы делаете это, то доступен специальный синтаксис.

Если вы предпочитаете, чтобы Django не создавал обратную связь, установите для related_name значение '+' или завершите его с помощью '+'. Например, это гарантирует, что модель User не будет иметь обратной связи с этой моделью:

user = models.ForeignKey(
    User,
    on_delete=models.CASCADE,
    related_name='+',
)


ForeignKey.related_query_name

Имя, используемое для обратного имени фильтра из целевой модели. По умолчанию используется значение related_name или default_related_name, если установлено, в противном случае по умолчанию используется имя модели:

# Declare the ForeignKey with related_query_name
class Tag(models.Model):
    article = models.ForeignKey(
        Article,
        on_delete=models.CASCADE,
        related_name="tags",
        related_query_name="tag",
    )
    name = models.CharField(max_length=255)

# That's now the name of the reverse filter
Article.objects.filter(tag__name="important")

Например related_name, related_query_name поддерживает интерполяцию меток приложения и классов посредством специального синтаксиса.


ForeignKey.to_field

Поле связанного объекта, к которому относится отношение. По умолчанию Django использует первичный ключ связанного объекта. Если вы ссылаетесь на другое поле, это поле должно иметь unique=True.


ForeignKey.db_constraint

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

  • У вас есть устаревшие данные, которые не действительны.
  • Вы разделяете свою базу данных.

Если для этого параметра установлено значение False, доступ к связанному объекту, который не существует, вызовет исключение DoesNotExist.


ForeignKey.swappable

Управляет реакцией фреймворка миграций, если этот ForeignKey указывает на заменяемую модель. Если это True — по умолчанию — тогда, если ForeignKey указывает на модель, которая соответствует текущему значению settings.AUTH_USER_MODEL (или другой сменной настройке модели), связь будет хранится в миграции с использованием ссылки на настройку, а не на модель напрямую.

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

Установка этого значения в False не означает, что вы можете ссылаться на заменяемую модель, даже если она поменяна местами — False означает, что миграции, выполненные с помощью этого ForeignKey, всегда будут ссылаться на указанную вами точную модель (так что это не удастся легко сломать, если пользователь пытается запустить с моделью User, которую вы не поддерживаете, например).

Если сомневаетесь, оставьте для него значение по умолчанию True.

ManyToManyField


class ManyToManyField(to, **options)[исходный код]¶

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

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

Использование в базе данных¶

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

Аргументы¶

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


ManyToManyField.related_name

То же, что ForeignKey.related_name.


ManyToManyField.related_query_name

То же, что ForeignKey.related_query_name.


ManyToManyField.limit_choices_to

То же, что ForeignKey.limit_choices_to.

limit_choices_to не влияет на использование ManyToManyField с пользовательской промежуточной таблицей, указанной с помощью параметра through.


ManyToManyField.symmetrical

Используется только в определении ManyToManyFields на самого себя. Рассмотрим следующую модель:

from django.db import models

class Person(models.Model):
    friends = models.ManyToManyField("self")

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

Если вам не нужна симметрия в отношениях «многие ко многим» с self, установите symmetrical в False. Это заставит Django добавить дескриптор для обратной связи, что сделает отношения ManyToManyField несимметричными.

Changed in Django 3.0:

Стало разрешено указывать symmetrical=True для рекурсивных отношений «многие ко многим» с использованием промежуточной модели.


ManyToManyField.through

Django автоматически создаст таблицу для управления отношениями «многие ко многим». Однако, если вы хотите указать промежуточную таблицу вручную, вы можете использовать опцию through, чтобы указать модель Django, представляющую промежуточную таблицу, которую вы хотите использовать.

Чаще всего этот параметр используется, когда вы хотите связать дополнительные данные с отношением «многие ко многим».

Примечание

If you don’t want multiple associations between the same instances, add
a UniqueConstraint including the from and to
fields. Django’s automatically generated many-to-many tables include
such a constraint.

Примечание

Рекурсивные отношения, использующие промежуточную модель и определяемые как симметричные (то есть с помощью symmetrical=False, которая используется по умолчанию), не могут определить имена обратных средств доступа, поскольку они будут одинаковыми. Вам необходимо установить related_name хотя бы для одного из них. Если вы предпочитаете, чтобы Django не создавал обратную связь, установите для related_name значение '+'.

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

Если исходная и целевая модели различаются, создаются следующие поля:

  • id: первичный ключ отношения.
  • <containing_model>_id: id модели, которая объявляет ManyToManyField.
  • <other_model>_id: « id« модели, на которую указывает ManyToManyField.

Если ManyToManyField указывает на одну и ту же модель, генерируются следующие поля:

  • id: первичный ключ отношения.
  • from_<model>_id: « id« экземпляра, который указывает на модель (то есть исходный экземпляр).
  • to_<model>_id: « id« экземпляра, на который указывает отношение (то есть экземпляр целевой модели).

This class can be used to query associated records for a given model
instance like a normal model:

Model.m2mfield.through.objects.all()


ManyToManyField.through_fields

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

from django.db import models

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

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

class Membership(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    inviter = models.ForeignKey(
        Person,
        on_delete=models.CASCADE,
        related_name="membership_invites",
    )
    invite_reason = models.CharField(max_length=64)

Membership имеет два внешних ключа для Person (person и inviter), что делает отношения неоднозначными, и Django не может знать, какой из них использовать. В этом случае вы должны явно указать, какие внешние ключи должен использовать Django, используя through_fields, как в примере выше.

through_fields принимает 2-х значный кортеж ('field1', 'field2'), где field1 — имя внешнего ключа модели, для которого определен класс ManyToManyField (group в данном случае) и field2 — имя внешнего ключа для целевой модели (в данном случае person).

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


ManyToManyField.db_table

Имя таблицы, создаваемой для хранения данных «многие ко многим». Если это не предусмотрено, Django примет имя по умолчанию на основе имен: таблицы для модели, определяющей отношение, и имени самого поля.


ManyToManyField.db_constraint

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

  • У вас есть устаревшие данные, которые не действительны.
  • Вы разделяете свою базу данных.

Будет ошибкой пропускать оба параметра: db_constraint и through.


ManyToManyField.swappable

Управляет реакцией фреймворка миграции, если этот ManyToManyField указывает на заменяемую модель. Если True — по умолчанию — тогда, если ManyToManyField указывает на модель, которая соответствует текущему значению settings.AUTH_USER_MODEL (или другой сменной настройки модели), отношение будет хранится в миграции с использованием ссылки на настройку, а не на модель напрямую.

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

Если сомневаетесь, оставьте для него значение по умолчанию True.

ManyToManyField не поддерживает validators.

null не имеет никакого эффекта, поскольку нет способа требовать отношений на уровне базы данных.

OneToOneField


class OneToOneField(to, on_delete, parent_link=False, **options)[исходный код]¶

Отношения один-к-одному. Концептуально это похоже на ForeignKey с unique=True, но «обратная» сторона отношения будет напрямую возвращать один объект.

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

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

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

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

from django.conf import settings
from django.db import models

class MySpecialUser(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    supervisor = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='supervisor_of',
    )

ваша итоговая модель User будет иметь следующие атрибуты:

>>> user = User.objects.get(pk=1)
>>> hasattr(user, 'myspecialuser')
True
>>> hasattr(user, 'supervisor_of')
True

A RelatedObjectDoesNotExist exception is raised when accessing the reverse
relationship if an entry in the related table doesn’t exist. This is a subclass
of the target model’s Model.DoesNotExist exception. For example, if a user
doesn’t have a supervisor designated by MySpecialUser:

>>> user.supervisor_of
Traceback (most recent call last):
    ...
RelatedObjectDoesNotExist: User has no supervisor_of.

Кроме того, OneToOneField принимает все дополнительные аргументы, принятые ForeignKey, плюс один дополнительный аргумент:


OneToOneField.parent_link

Когда True и используется в модели, которая наследуется от другого concrete model, указывает, что это поле следует использовать как ссылку на родительский класс, а не как дополнительное OneToOneField, которое обычно неявно создается путем создания подклассов.

Смотрите Отношения один-к-одному для примеров использования OneToOneField.

Как указать значение по умолчанию для поля Django ForeignKey Model или AdminModel? Ru Python

Как установить значение по умолчанию в поле ForeignKey в модели django или в AdminModel?

Что-то вроде этого (но, конечно, это не работает) …

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

 class Foo(models.Model): a = models.CharField(max_length=42) class Bar(models.Model): b = models.CharField(max_length=42) a = models.ForeignKey(Foo, default=lambda: Foo.objects.get(id=1) ) 

Вот решение, которое будет работать в Django 1.7. Вместо того, чтобы указывать значение по умолчанию в определении поля, установите его как null-able, но переопределите функцию «сохранить», чтобы заполнить его в первый раз (пока он равен нулю):

 class Foo(models.Model): a = models.CharField(max_length=42) class Bar(models.Model): b = models.CharField(max_length=42) a = models.ForeignKey(Foo, null=True) def save(self, *args, **kwargs): if self.a is None: # Set default reference self.a = Foo.objects.get(id=1) super(Bar, self).save(*args, **kwargs) 

Если вы используете версию Django для разработки, вы можете реализовать метод formfield_for_foreignkey() в вашем AdminModel чтобы установить значение по умолчанию.

Для django 1.7 или выше,

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

Например,

 created_by = models.ForeignKey(User, default=1) 

Я сделал это аналогично @o_c, но я предпочел бы использовать get_or_create чем просто pk .

 class UserSettings(models.Model): name = models.CharField(max_length=64, unique=True) # ... some other fields @staticmethod def get_default_user_settings(): user_settings, created = UserSettings.objects.get_or_create( name = 'Default settings' ) return user_settings class SiteUser(...): # ... some other fields user_settings = models.ForeignKey( to=UserSettings, on_delete=models.SET_NULL, null=True ) def save(self, *args, **kwargs): if self.user_settings is None: self.user_settings = UserSettings.get_default_params() super(SiteUser, self).save(*args, **kwargs) 

Что касается меня, то для Django 1.7 его работа, просто pk:

category = models.ForeignKey(Category, editable=False, default=1)

но помните, что миграция выглядит

 migrations.AlterField( model_name='item', name='category', field=models.ForeignKey(default=1, editable=False, to='item.Category'), preserve_default=True, ), 

поэтому, я не думаю, что это должно работать с динамическим пользователем pk.

Это небольшая модификация ответа от o_c . Он должен сэкономить вам одно попадание в базу данных. Обратите внимание, что в методе сохранения я использую self.a_id = 1 вместо self.a = Foo.objects.get (id = 1). Он имеет тот же эффект, не запрашивая Foo.

 class Foo(models.Model): a = models.CharField(max_length=42) class Bar(models.Model): b = models.CharField(max_length=42) a = models.ForeignKey(Foo, null=True) def save(self, *args, **kwargs): if self.a is None: # Set default reference self.a_id = 1 super(Bar, self).save(*args, **kwargs) 
 def get_user_default_id(): return 1 created_by = models.ForeignKey(User, default=get_user_default_id) 
 class table1(models.Model): id = models.AutoField(primary_key=True) agentname = models.CharField(max_length=20) class table1(models.Model): id = models.AutoField(primary_key=True) lastname = models.CharField(max_length=20) table1 = models.ForeignKey(Property) 

Просто о Django Content Types Framework

Ali Aliyev
[2015-06-04]

Появилась задача реализовать функцию комментариев для пяти моделей через одну.
Небольшой пример, как это могло выглядеть без Content Types Framework:

class Comment(models.Model):
    user = models.ForeignKey('User', related_name='comment')
    title = models.CharField(max_length=255)
    body = models.TextField()

    article = models.ForeignKey('Article', related_name='comment')
    todo = models.ForeignKey('ToDo', related_name='comment')
    blog_post = models.ForeignKey('BlogPost', related_name='comment')
    image = models.ForeignKey('Image', related_name='comment')
    album = models.ForeignKey('Album', related_name='comment')

Пример с Content Types:

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class Comment(models.Model):
    user = models.ForeignKey('User', related_name='comment')
    title = models.CharField(max_length=255)
    body = models.TextField()

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

Куда более компактный код, не так ли? И так, что же такое Content Types Framework и как он работает?

Content Types Framework сохраняет информацию о моделях в таблицу contenttypes, а именно: название приложения, название модели и тип.

Таким образом мы можем создать GenericForeignKey на любую модель используя всего одно поле.

Получить объект Content Type можно двумя способами:

по классу нашей модели

ContentType.objects.get_for_model(Article)

или по названию модели

from django.contrib.contenttypes.models import ContentType
article_type = ContentType.objects.get(app_label='core', model='article')

теперь можно получить модель через Content Type Object:

model = user_type.model_class()

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

class MyTypes(View):
    def get(self, request, *args, **kwargs):
        content_type_name = kwargs.get('name')

        # Получаем content type по названию модели из url
        content_type = get_object_or_404(ContentType, name=content_type_name)

        # Получаем модель
        model = content_type.get_for_model()
        context = {
            'objects': model.objects.all()
        }

Так можно получить список комментариев определенной статьи

first_article = Article.objects.get(pk=1)
content_type = ContentType.objects.get_for_model(first_article)
object_list = Comment.objects.filter(
    content_type__pk=content_type.pk,
    object_id=first_article.pk
)

django.db.models Примеры кода Python ForeignKey

ForeignKey
это Django ORM преобразование полей в столбцы для
создание и работа с отношениями между таблицами в
реляционные базы данных.

ForeignKey определяется в
django.db.models.related
модуль, но обычно на него ссылаются
django.db.models
вместо использования ссылки на модуль , относящейся к .

Пример 1 из AuditLog

Журнал аудита
(проектная документация)
это приложение Django, которое регистрирует изменения в объектах Python,
похожи на журналы администратора Django, но с более подробной информацией и
форматы вывода.Исходный код Auditlog предоставляется с открытым исходным кодом под лицензией
Лицензия MIT.

AuditLog / src / auditlog_tests / models.py

 импорт uuid

из django.contrib.postgres.fields импортировать ArrayField
из моделей импорта django.db
из auditlog.models импортировать AuditlogHistoryField
из auditlog.registry импортировать журнал аудита

из множественного выбора поля импорта MultiSelectField


## ... сокращенный исходный файл, чтобы перейти к примерам ForeignKey ...


класс RelatedModel (models.Model):
    "" "
    Модель с внешним ключом."" "

    related = models.ForeignKey (to = 'self', on_delete = models.CASCADE)

    history = AuditlogHistoryField ()


класс ManyRelatedModel (models.Model):
    "" "
    Модель с отношением "многие ко многим".
    "" "

    related = models.ManyToManyField ('сам')

    history = AuditlogHistoryField ()


@ auditlog.register (include_fields = ['label'])
класс SimpleIncludeModel (models.Model):
    "" "
    Простая модель, используемая для регистра include_fields kwarg
    "" "

    label = models.CharField (max_length = 100)
    текст = модели.Текстовое поле (пустое = True)

    history = AuditlogHistoryField ()


класс SimpleExcludeModel (models.Model):
    "" "
    Простая модель, используемая для регистра exclude_fields kwarg
    "" "

    label = models.CharField (max_length = 100)
    text = models.TextField (blank = True)

    history = AuditlogHistoryField ()


класс SimpleMappingModel (models.Model):
    "" "
    Простая модель, используемая для регистров mapping_fields kwarg
    "" "

    sku = models.CharField (max_length = 100)
    vtxt = models.CharField (verbose_name = 'Version', max_length = 100)
    not_mapped = модели.CharField (max_length = 100)

    history = AuditlogHistoryField ()


класс AdditionalDataIncludedModel (models.Model):
    "" "
    Модель, в которой определена get_additional_data, позволяющая регистрировать дополнительные
    информация о модели в JSON
    "" "

    label = models.CharField (max_length = 100)
    text = models.TextField (blank = True)
    related = models.ForeignKey (to = SimpleModel, on_delete = models.CASCADE)

    history = AuditlogHistoryField ()

    def get_additional_data (сам):
        "" "
        Возвращает JSON, который фиксирует снимок дополнительных сведений о
        экземпляр модели.К этому методу, если он определен, обращается журнал аудита.
        manager и добавляется в каждый экземпляр журнала при создании.
        "" "
        object_details = {
            'related_model_id': self.related.id,
            'related_model_text': self.related.text
        }
        вернуть object_details


# ... исходный файл продолжается без дополнительных примеров ForeignKey ...
 

Пример 2 из dccnsys

dccnsys - это регистрация на конференцию
система, созданная с помощью Django. Код с открытым исходным кодом под
Лицензия MIT.

dccnsys / wwwdccn / review / models.py

 из настроек импорта django.conf
из django.core.mail импорт send_mail
из моделей импорта django.db
из django.db.models.signals импорт post_save, post_delete
из приемника импорта django.dispatch
из django.template.loader import render_to_string
из django.utils.translation импортируйте ugettext_lazy как _

от Conferences.models import Conference
из submissions.models импорт Представление
из users.models import User


# Создайте свои модели здесь.класс Reviewer (models.Model):
    user = models.ForeignKey (Пользователь, on_delete = models.CASCADE)
    конференция = models.ForeignKey (Conference, on_delete = models.CASCADE)


ОЦЕНКА = (
    ('1', _ ('1 - очень плохо')),
    ('2', _ ('2 - Ниже среднего')),
    ('3', _ ('3 - Среднее')),
    ('4', _ ('4 - Хорошо')),
    ('5', _ ('5 - Отлично')),
)


Обзор класса (models.Model):
    NUM_SCORES = 4

    # Коды выбора очков:
    ПЛОХО = 1
    BELOW_AVERAGE = 2
    СРЕДНИЙ = 3
    ХОРОШО = 4
    ОТЛИЧНО = 5

    SCORE_CHOICES = (
        ('', _ ('Выберите свой результат')),
        (str (ПЛОХО), _ ('1 - Очень плохо')),
        (str (BELOW_AVERAGE), _ ('2 - Ниже среднего')),
        (str (СРЕДНИЙ), _ ('3 - Среднее')),
        (str (ХОРОШО), _ ('4 - Хорошо')),
        (str (ОТЛИЧНО), _ ('5 - Отлично'))
    )

    обозреватель = модели.Иностранный ключ(
        Рецензент,
        on_delete = models.CASCADE,
        related_name = 'отзывы',
    )

    paper = models.ForeignKey (
        Подчинение,
        on_delete = models.CASCADE,
        related_name = 'отзывы',
    )

    Technical_merit = models.CharField (
        choices = SCORE_CHOICES,
        max_length = 1,
        blank = True,
    )

    четкость = models.CharField (
        choices = SCORE_CHOICES,
        max_length = 1,
        blank = True,
    )

    релевантность = models.CharField (
        choices = SCORE_CHOICES,
        max_length = 1,
        blank = True,
    )

    оригинальность = модели.CharField (
        choices = SCORE_CHOICES,
        max_length = 1,
        blank = True,
    )

    details = models.TextField ()

    отправлено = models.BooleanField (по умолчанию = False)

    def __str __ (сам):
        name = self.reviewer.user.profile.get_full_name ()
        вернуть f'Review for submission # {self.paper.pk} by {name} '

    def check_details (самостоятельно):
        вернуть check_review_details (self.details, self.paper.stype)

    def score_fields (сам):
        возвращаться {
            'техническое_мертение': сам.Technical_merit,
            'ясность': самость. ясность,
            'оригинальность': самобытность. оригинальность,
            'релевантность': самостоятельная релевантность,
        }

    def missing_score_fields (самостоятельно):
        вернуть кортеж (k для k, v в self.score_fields (). items (), если v == '')

    def all_scores_filled (самостоятельно):
        вернуть self.num_scores_missing () == 0

    def num_scores_missing (сам):
        вернуть len (self.missing_score_fields ())

    def предупреждения (самостоятельно):
        num_missing = self.num_scores_missing ()
        предупреждения = []
        если num_missing == Обзор.NUM_SCORES, а не self.details:
            warnings.append ('Пожалуйста, начните обзор')
        еще:
            fill_details = self.check_details ()
            fill_scores = num_missing == 0

            если не заполнено_счетов:
                warnings.append (
                    f "Не заполнено {num_missing} из {Review.NUM_SCORES} баллов"
                )
            если не fill_details:
                warnings.append ('Сведения о проверке неполны')
            если fill_scores и fill_details, а не self.Отправлено:
                warnings.append ('Отзыв еще не отправлен')
        возвращать предупреждения

    def average_score (самостоятельно):
        если self.all_scores_filled ():
            fields = self.score_fields ()
            вернуть сумму (int (x) для x в fields.values ​​()) / len (fields)
        возврат 0

## ... исходный файл продолжается без дополнительных примеров ForeignKey ...
 

Пример 3 из django-allauth

джанго-аллаут
(сайт проекта) является
Библиотека Django для простого добавления локальной и социальной аутентификации
потоки в проекты Django.Это открытый исходный код под
Лицензия MIT.

джанго-аллаут / allauth / socialaccount / models.py

 из __future__ import absolute_import

из django.contrib.auth импорт аутентификации
с сайта импорта django.contrib.sites.models
из django.contrib.sites.shortcuts импортировать get_current_site
из django.core.exceptions import PermissionDenied
из моделей импорта django.db
из django.utils.crypto импортировать get_random_string

импортировать allauth.app_settings
из allauth.account.модели импортировать EmailAddress
из allauth.account.utils import get_next_redirect_url, setup_user_email
from allauth.compat import (
    force_str,
    python_2_unicode_compatible,
    ugettext_lazy как _,
)
из allauth.utils импортировать get_user_model

from ..utils import get_request_param
из . импорт app_settings, провайдеров
из .adapter import get_adapter
из .fields импортировать JSONField


## ... сокращенный исходный файл, чтобы перейти к примерам ForeignKey ...


@ python_2_unicode_compatible
класс SocialAccount (models.Модель):
    user = models.ForeignKey (allauth.app_settings.USER_MODEL,
                             on_delete = models.CASCADE)
    provider = models.CharField (verbose_name = _ ('поставщик'),
                                max_length = 30,
                                choices = provider.registry.as_choices ())
    # На всякий случай вам интересно, собирается ли URL-адрес идентификации OpenID
    # чтобы вписаться в 'uid':
    #
    # В идеале должно использоваться URLField (max_length = 1024, unique = True)
    # для идентичности. Однако MySQL имеет ограничение max_length равным 191
    # для URLField (в случае utf8mb4).Как насчет
    Тогда # models.TextField (unique = True)? Ну это не сработает
    # либо для MySQL из-за другой ошибки [1]. Так что единственный выход
    # было бы отбросить уникальное ограничение или переключиться на более короткий
    # идентификационных URL. Сделал выбор в пользу последнего, поскольку [2] предполагает, что
    # идентификационные URL в любом случае должны быть короткими, по крайней мере, для
    # старая спец.
    #
    # [1] http://code.djangoproject.com/ticket/2495.
    # [2] http://openid.net/specs/openid-authentication-1_1.html#limits

    uid = модели.CharField (verbose_name = _ ('uid'),
                           max_length = app_settings.UID_MAX_LENGTH)
    last_login = models.DateTimeField (verbose_name = _ ('последний вход'),
                                      auto_now = True)
    date_joined = models.DateTimeField (verbose_name = _ ('дата присоединения'),
                                       auto_now_add = True)
    extra_data = JSONField (verbose_name = _ ('дополнительные данные'), по умолчанию = dict)

    класс Мета:
        unique_to together = ('провайдер', 'uid')
        verbose_name = _ ('социальная учетная запись')
        verbose_name_plural = _ ('социальные аккаунты')

    def Authenticate (сам):
        вернуть аутентификацию (account = self)

    def __str __ (сам):
        return force_str (self.Пользователь)

    def get_profile_url (сам):
        вернуть self.get_provider_account (). get_profile_url ()

    def get_avatar_url (сам):
        вернуть self.get_provider_account (). get_avatar_url ()

    def get_provider (сам):
        вернуть Provider.registry.by_id (self.provider)

    def get_provider_account (сам):
        вернуть self.get_provider (). wrap_account (сам)


@ python_2_unicode_compatible
класс SocialToken (models.Model):
    app = models.ForeignKey (SocialApp, on_delete = models.CASCADE)
    аккаунт = модели.ForeignKey (SocialAccount, on_delete = models.CASCADE)
    token = models.TextField (
        verbose_name = _ ('токен'),
        help_text = _ (
            '"oauth_token" (OAuth2) или токен доступа (OAuth3)'))
    token_secret = models.TextField (
        blank = True,
        verbose_name = _ ('секретный токен'),
        help_text = _ (
            '"oauth_token_secret" (OAuth2) или токен обновления (OAuth3)'))
    expires_at = models.DateTimeField (blank = True, null = True,
                                      verbose_name = _ ('истекает в'))

    класс Мета:
        unique_toght = ('приложение', 'учетная запись')
        verbose_name = _ ('токен социального приложения')
        verbose_name_plural = _ ('токены социальных приложений')

    def __str __ (сам):
        вернуть себя.жетон


## ... исходный файл продолжается без дополнительных примеров ForeignKey ...
 

Пример 4 из django-cms

джанго-cms
(веб-сайт проекта) основан на Python
библиотека системы управления контентом (CMS)
для использования с веб-приложениями Django с открытым исходным кодом под
BSD 3-Clause "New"
лицензия.

django-cms / cms / migrations / 0018_create_pagenode.py

 # - * - кодировка: utf-8 - * -
# Создано Django 1.10.8, 2018-01-03 19:50
from __future__ импортировать unicode_literals

импортировать django
импортировать django.contrib.auth.models
из django.db импортировать миграции, модели
импортировать django.db.models.deletion

из . import IrreversibleMigration


def get_descendants (корень):
    "" "
    Возвращает генератор первичных ключей, которые представляют
    потомки данного идентификатора страницы (root_id)
    "" "
    # Обратите внимание, что это сделано, потому что get_descendants () нельзя доверять
    # так как дерево может быть повреждено.

    для дочернего элемента в root.children.order_by ('path'). iterator ():
        давать ребенка

        для ребенка в get_descendants (child):
            давать ребенка


def create_page_nodes (приложения, schema_editor):
    Страница = приложения.get_model ('cms', 'Страница')
    TreeNode = apps.get_model ('cms', 'TreeNode')
    db_alias = schema_editor.connection.alias
    root_draft_pages = Page.objects.using (db_alias) .filter (
        publisher_is_draft = Верно,
        parent__isnull = Верно,
    )

    create_node = TreeNode.objects.using (db_alias) .create

    nodes_by_page = {}

    для root в root_draft_pages:
        узел = create_node (
            site_id = root.site_id,
            путь = root.path,
            depth = root.depth,
            numchild = корень.numchild,
            parent = None,
        )

        nodes_by_page [root.pk] = узел

        для потомка в get_descendants (root):
            узел = create_node (
                site_id = потомок.site_id,
                путь = Потомок.путь,
                глубина = потомок. глубина,
                numchild = потомок. numchild,
                parent = nodes_by_page [Потомок.parent_id],
            )
            узлы_бай_страница [потомок] = узел


Миграция класса (IrreversibleMigration):

    зависимости = [
        ('сайты', '0001_initial'),
        ('cms', '0017_pagetype'),
    ]
    replaces = [('cms', '0018_pagenode')]

    операции = [
        миграции.CreateModel (
            name = 'TreeNode',
            поля = [
                ('id', models.AutoField (auto_created = True, primary_key = True, serialize = False, verbose_name = 'ID')),
                ('путь', models.CharField (max_length = 255, unique = True)),
                ('глубина', models.PositiveIntegerField ()),
                ('numchild', models.PositiveIntegerField (по умолчанию = 0)),
                ('родительский', models.ForeignKey (blank = True, null = True, on_delete = django.db.models.deletion.CASCADE, related_name = 'children', to = 'cms.TreeNode ')),
                ('сайт', models.ForeignKey (on_delete = django.db.models.deletion.CASCADE, related_name = 'djangocms_nodes', to = 'sites.Site', verbose_name = 'site')),
            ],
            options = {
                'порядок': ('путь',),
                'default_permissions': [],
            },
        ),
        migrations.RunPython (create_page_nodes),
        migrations.AddField (
            model_name = 'страница',
            name = 'узел',
            field = models.ForeignKey (null = True, on_delete = django.db.models.deletion.CASCADE, related_name = 'cms_pages',
                                    to = 'cms.TreeNode'),
        ),
        migrations.AddField (
            model_name = 'страница',
            name = 'migration_0018_control',
            field = models.PositiveIntegerField (null = True),
        ),
        миграции.AlterUniqueTogether (
            name = 'page',
            unique_to together = set ([('узел', 'publisher_is_draft')]),
        ),
        миграции.
            name = 'pageusergroup',
            менеджеры = [
                ('объекты', django.contrib.auth.models.GroupManager ()),
            ],
        ),
    ]
 

Пример 5 от django-guardian

джанго-хранитель
(проектная документация
и
Страница PyPI)
предоставляет разрешения для каждого объекта в проектах Django
за счет улучшения существующей серверной части аутентификации. Код проекта
является открытым исходным кодом под
Лицензия MIT.

джанго-хранитель / опекун / миграции / 0001_initial.py

 # 0001_initial.py
из моделей импорта django.db, миграции
из джанго.conf настройки импорта


Миграция класса (migrations.Migration):

    зависимости = [
        ('типы содержимого', '0001_initial'),
        ('аутентификация', '0001_initial'),
        migrations.swappable_dependency (settings.AUTH_USER_MODEL),
    ]

    операции = [
        migrations.CreateModel (
            name = 'GroupObjectPermission',
            поля = [
                ('id', models.AutoField (primary_key = True,
                                        serialize = False, auto_created = True, verbose_name = 'ID')),
                ('объект_пк', модели.CharField (
                    max_length = 255, verbose_name = 'идентификатор объекта')),
                ('content_type', models.ForeignKey (to = 'contenttypes.ContentType', on_delete = models.CASCADE)),
                ('группа', models.ForeignKey (to = 'auth.Group', on_delete = models.CASCADE)),
                ('разрешение', models.ForeignKey (to = 'auth.Permission', on_delete = models.CASCADE)),
            ],
            options = {
            },
            базы = (models.Model,),
        ),
        migrations.CreateModel (
            name = 'UserObjectPermission',
            поля = [
                ('id', модели.АвтоПоле (primary_key = True,
                                        serialize = False, auto_created = True, verbose_name = 'ID')),
                ('объект_пк', models.CharField (
                    max_length = 255, verbose_name = 'идентификатор объекта')),
                ('content_type', models.ForeignKey (to = 'contenttypes.ContentType', on_delete = models.CASCADE)),
                ('разрешение', models.ForeignKey (to = 'auth.Permission', on_delete = models.CASCADE)),
                ('пользователь', models.ForeignKey (to = settings.AUTH_USER_MODEL, on_delete = models.CASCADE)),
            ],
            options = {
            },
            базы = (models.Model,),
        ),
        миграции.AlterUniqueTogether (
            name = 'userobjectpermission',
            unique_to together = set ([('пользователь', 'разрешение', 'object_pk')]),
        ),
        миграции.AlterUniqueTogether (
            name = 'groupobjectpermission',
            unique_to together = set ([('группа', 'разрешение', 'object_pk')]),
        ),
    ]
 

Пример 6 из django-jet

джанго-джет
(проектная документация,
Страница проекта PyPI и
Дополнительная информация)
Замечательная замена панели администратора Django.

Проект django-jet с открытым исходным кодом под
Стандартная общественная лицензия GNU Affero v3.0.

джанго-джет / джет / тесты / models.py

 # models.py
из моделей импорта django.db
из django.utils.encoding import python_2_unicode_compatible


@ python_2_unicode_compatible
класс TestModel (models.Model):
    field1 = models.CharField (max_length = 255)
    field2 = models.IntegerField ()

    def __str __ (сам):
        вернуть '% s% d'% (self.field1, self.field2)


@ python_2_unicode_compatible
класс RelatedToTestModel (models.Модель):
    field = models.ForeignKey (TestModel, on_delete = models.CASCADE)

    def __str __ (сам):
        вернуть self.field


@ python_2_unicode_compatible
класс SearchableTestModel (models.Model):
    field1 = models.CharField (max_length = 255)
    field2 = models.IntegerField ()

    def __str __ (сам):
        вернуть '% s% d'% (self.field1, self.field2)

    @staticmethod
    def autocomplete_search_fields ():
        вернуть 'field1'
 

Пример 7 от django-smithy

django-smithy - это
библиотека кода Django, которая позволяет пользователям отправлять
HTTP-запросы из пользовательского интерфейса администратора Django.Код для
проект с открытым исходным кодом под
Лицензия MIT.

django-smithy / smithy / migrations / 0002_auto_201

_1052_squashed_0008_auto_201

_1213.py

 # Сгенерировано Django 2.1.5 в 17:14 2019-03-17

из django.db импортировать миграции, модели
импортировать django.db.models.deletion
импортировать django.utils.timezone
импорт model_utils.fields


Миграция класса (migrations.Migration):

    replaces = [('smithy', '0002_auto_201

_1052'), ('smithy', '0003_auto_201

_1103'), ('smithy', '0004_auto_201

_1104'), ('smithy', '0005_auto_body', '0005_auto_106ithy_type', '0005_auto_106ithy_type', 0005_auto_106ithy '), (' кузница ',' 0007_auto_201

_1159 '), (' кузница ',' 0008_auto_201

_1213 ')] зависимости = [ ('кузница', '0001_initial'), ] операции = [ миграции.CreateModel ( name = 'BodyParameter', поля = [ ('id', models.AutoField (auto_created = True, primary_key = True, serialize = False, verbose_name = 'ID')), ('created', model_utils.fields.AutoCreatedField (по умолчанию = django.utils.timezone.now, editable = False, verbose_name = 'created')), ('изменено', model_utils.fields.AutoLastModifiedField (по умолчанию = django.utils.timezone.now, editable = False, verbose_name = 'изменено')), ('имя', модели.CharField (max_length = 200)), ('значение', models.TextField ()), ('запрос', models.ForeignKey (on_delete = django.db.models.deletion.CASCADE, related_name = 'body_parameters', to = 'smithy.RequestBlueprint')), ], options = { 'abstract': Ложь, }, ), migrations.AddField ( model_name = 'requestrecord', name = 'raw_request', field = models.TextField (по умолчанию = ''), ), миграции.RemoveField ( model_name = 'requestrecord', name = 'ответ', ), migrations.AddField ( model_name = 'requestrecord', name = 'статус', field = models.PositiveIntegerField (null = True), ), migrations.AddField ( model_name = 'requestrecord', name = 'raw_response', field = models.TextField (по умолчанию = ''), preserve_default = Ложь, ), migrations.AddField ( model_name = 'запрос', name = 'content_type', поле = модели.CharField (blank = True, choices = [('', 'Другое'), ('application / x-www-form-urlencoded', 'x-www-form-urlencoded'), ('application / json', ' JSON '), (' text / plain ',' Text '), (' application / javascript ',' JavaScript '), (' application / xml ',' XML (application / xml) '), (' text / xml ',' XML (text / xml) '), (' text / html ',' HTML ')], по умолчанию =' ', max_length = 100, null = True), ), ]

Пример 8 с гаджет-платы

гаджет-плата - это
Джанго,
Django REST Framework (DRF) и
Веб-приложение Angular с открытым исходным кодом под
Лицензия Apache2.

гаджет-доска / веб / гаджеты / models.py

 # models.py
из моделей импорта django.db
из django.contrib.postgres.fields импортировать JSONField
из django.template.defaultfilters import slugify
from authentication.models import Account


класс Гаджет (models.Model):
    name = models.CharField (max_length = 40, unique = True)
    slug = models.SlugField (null = True, blank = True)
    description = models.TextField ()
    users_can_upload = models.ManyToManyField (Аккаунт)
    image_name = модели.CharField (max_length = 140, пусто = True)
    created_at = models.DateTimeField (auto_now_add = True)
    updated_at = models.DateTimeField (auto_now = True)

    @свойство
    def image_url (сам):
        если self.image_name! = "":
            вернуть "backend / static / media / {}". format (self.image_name)
        еще:
            вернуть "backend / static / dashboard_icon_big.png"

    def __str __ (сам):
        вернуть self.name

    def save (self, * args, ** kwargs):
        если не self.id:
            self.slug = slugify (self.имя)

        super (Гаджет, сам) .save (* args, ** kwargs)


класс GadgetData (models.Model):
    gadget = models.ForeignKey (Gadget, db_index = True, on_delete = models.DO_NOTHING) # Добавить индекс по отфильтрованным полям
    данные = JSONField ()
    added_by = models.ForeignKey (Аккаунт, on_delete = models.DO_NOTHING)
    timestamp = models.DateTimeField (null = True, blank = True, db_index = True) # Добавить индекс по отфильтрованным полям
    created_at = models.DateTimeField (auto_now_add = True)

    def __str __ (сам):
        возвращаться '{} {} {}'.формат (self.gadget, self.timestamp, self.added_by)
 

Django - ForeignKey | Учебник django

Пример

Поле

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

  из моделей импорта django.db

класс Person (models.Model):
    GENDER_FEMALE = 'F'
    GENDER_MALE = 'М'

    GENDER_CHOICES = (
        (GENDER_FEMALE, "Женский"),
        (GENDER_MALE, "Мужской"),
    )

    first_name = модели.CharField (max_length = 100)
    last_name = models.CharField (max_length = 100)
    пол = models.CharField (max_length = 1, choices = GENDER_CHOICES)
    возраст = models.SmallIntegerField ()


класс Car (модель.Model)
    owner = models.ForeignKey ('Человек')
    plate = models.CharField (max_length = 15)
    brand = models.CharField (max_length = 50)
    model = models.CharField (max_length = 50)
    цвет = models.CharField (max_length = 50)
  

Первый аргумент поля - это класс, к которому относится модель.Второй позиционный аргумент - аргумент on_delete .
В текущих версиях этот аргумент не требуется, но он потребуется в Django 2.0.
Функциональные возможности аргумента по умолчанию показаны следующим образом:

Автомобиль класса

  (модель.
    owner = models.ForeignKey ('Человек', on_delete = models.CASCADE)
    ...
  

Это приведет к тому, что объекты Car будут удалены из модели, когда их владелец будет удален из модели Person.Это функция по умолчанию.

Автомобиль класса

  (модель.
    owner = models.ForeignKey ('Человек', on_delete = models.PROTECT)
    ...
  

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

джанго-составной-иностранный ключ · PyPI

позволяет создать внешний ключ django, который не связан с pk другой модели, но с несколькими столбцами, соответствующими столбцам локальной модели или фиксированным значениям.

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

сегодня, Django не поддерживает составной первичный ключ (см. Тикет), а ForeignKey не поддерживает несколько столбцов.
но, к счастью, базовый класс ForeignKey хорошо его поддерживает, поэтому эта библиотека просто добавляет небольшую оболочку вокруг ForeignObject, чтобы сделать ее более полезной.
реальное добавление этой реализации - поддержка настройки ссылки с помощью необработанных значений.

, эта реализация CompositeForeignKey пропускает сложность составного первичного ключа, принудительно предоставляя соответствующий столбец другой модели, а не принудительно PrimaryKey.

Установка

  1. Установить с помощью pip:

    pip install django-Composite-foreignkey

  2. В качестве альтернативы вы можете установить, загрузить или клонировать это репо и позвонить по номеру

    pip install -e ..

Пример

у вас есть эта модель

Заказчик класса

 (models.Model):

    company = models.IntegerField ()
    customer_id = models.IntegerField ()
    name = models.CharField (max_length = 255)
    адрес = CompositeForeignKey (Адрес, on_delete = CASCADE, to_fields = {
        "tiers_id": "customer_id",
        «компания»: LocalFieldValue («компания»),
        "type_tiers": RawFieldValue ("C")
    })

    класс Meta (объект):
        unique_to together = [
            ("компания", "customer_id"),
        ]


класс Контакт (модели.Модель):
    company_code = models.IntegerField ()
    customer_code = models.IntegerField ()
    surname = models.CharField (max_length = 255)
    # виртуальное поле
    customer = CompositeForeignKey (Клиент, on_delete = CASCADE, related_name = 'contacts', to_fields = {
        "customer_id": "customer_code",
        "компания": "код_компании"
    })
 

, вы можете использовать Contact.customer как любой ForeignKey, но за сценой он будет запрашивать таблицу клиентов, используя идентификаторы компании и клиента.

Требования

  • Python 2.7, 3,4, 3,5, 3,6, 3,7
  • Джанго 1.11, 2.0, 2.1

Приветствуются предложения и запросы на включение других версий Django и Python.

Лицензия

Вы можете использовать это под GPLv3.

Спасибо

Спасибо django за этот замечательный фреймворк. И благодаря django-bootstrap3 за структуру приложений.

Целостность базы данных Django: параметры ForeignKey on_delete | by Inem Patrick

Django поставляется с очень надежным Object Relational Mapper (ORM) для управления системами управления реляционными базами данных.Я буду писать о параметрах Django ForeignKey on_delete для определения того, как база данных обрабатывает удаление ссылочных данных, чтобы поддерживать целостность данных.

Параметры для on_delete включают:

1 CASCADE

2 PROTECT

3 SET_NULL

4 SET_DEFAULT

5 SET ()

6 DO_NOTHING

Мы собираемся продемонстрировать две модели. чехлы для вышеперечисленных вариантов. Модель Post и модель Comment .

Модель комментариев и постов

Я определил сериализаторы и представления для поста и комментария соответственно. Для краткости я их опустил.

Я использую расширяемый API для выполнения запросов к серверу. У меня есть браузер БД для SQLite, и я буду показывать содержимое базы данных по мере продвижения.

CASCADE

Cascade эмулирует ограничение SQL ON DELETE CASCADE . Всякий раз, когда объект, на который имеется ссылка (сообщение), удаляется, объекты, на которые он ссылается (комментарии), также удаляются.Такое поведение является разумным по умолчанию и имеет смысл для большинства отношений, в том числе и для этого. Вы не хотите, чтобы потерянные комментарии валялись в вашей базе данных, когда связанная запись удалена.

Я собираюсь создать новое сообщение и добавить к нему комментарий ниже:

Вот экземпляр сообщения в базе данных:

И вот комментарий:

Это комментарий, относящийся к недавно созданному сообщению. Помните, что мы используем on_delete аргумент CASCADE .Теперь мы собираемся удалить сообщение и ожидаем, что вместе с ним будет и комментарий.

Если вы заметили, в доступном для просмотра API есть красная кнопка УДАЛИТЬ . Это присутствует, потому что я определил конечную точку DELETE для объекта post.

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

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

PROTECT

Аргумент PROTECT параметра ForeignKey on_delete предотвращает удаление указанного объекта, если он уже имеет объект, ссылающийся на него в базе данных. Проще говоря, Django предотвратит удаление публикации, если к ней уже есть комментарии.

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

Важно отметить, что, когда вы пытаетесь удалить сообщение с комментарием (-ами), и оно имеет ограничение on_delete PROTECT, оно вызовет PROTECTEDERROR , с которым вы можете справиться, когда создание ваших просмотров.

Давайте изменим наш код и проверим все, что мы только что сказали.

Мы собираемся внести следующие изменения:

Мы изменили аргумент с CASCADE на PROTECT .

Затем мы переносим изменения для отражения в базе данных с помощью:

 python manage.py makemigrationspython manage.py migrate 

Теперь в базе данных был создан новый экземпляр POST с комментарием только для ускорения процесса. .

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

Теперь, когда мы получили эту милую, очень описательную ошибку, единственный способ успешно удалить сообщение - это сначала удалить комментарии.Перемещаю зудящие пальцы к нашей красивой красной кнопке DELETE , чтобы удалить комментарий к сообщению:

Теперь сообщение можно стереть без ошибок. Милая!

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

SET_NULL

Аргумент SET_NULL для ForeignKey on_delete опция доступна только в том случае, если вы установили для нулевой опции в поле ForeignKey значение True . Когда вы используете этот аргумент и, в нашем случае, удаляете сообщение, он оставляет комментарии в базе данных, не удаляя их.

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

Я обновил поле post ForeignKey с PROTECT до SET_NULL, а также установил null = True.

 python manage.py makemigrationspython manage.py migrate 

Создано сообщение с комментарием ниже:

Теперь, когда я удаляю сообщение из просматриваемого API, для указанного сообщения в таблице комментариев устанавливается значение null

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

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

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

SET_DEFAULT

Этот аргумент в опции ForeignKey on_delete требует, чтобы вы установили значение по умолчанию при определении отношения.Когда вы удаляете сообщение с комментариями, комментарии автоматически назначаются сообщению по умолчанию, которое вы установили при создании модели.

Вам необходимо установить параметр ПО УМОЛЧАНИЮ для ForeignKey. Мы обновляем нашу модель следующим образом:

Создано два сообщения, сообщение по умолчанию и другое сообщение, чтобы продемонстрировать это:

Обратите внимание, что этот комментарий относится к сообщению с идентификатором id 6.

После удаления 6-го сообщения, комментарий переназначается на значение по умолчанию id 5, которое является значением по умолчанию, которое мы установили в модели.Круто!

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

Этот пример ниже был получен из документации Django.

Когда вы удаляете указанного пользователя, пользователь с именем «удаленный» назначается экземпляру MyModel , который ссылается на него.

DO_NOTHING
Как следует из названия, он ничего не делает при удалении объекта, на который указывает ссылка.По сути, это не рекомендуется, потому что это противоречит цели СУБД. Ваши комментарии по-прежнему ссылаются на несуществующие сообщения, что вызывает множество ошибок и ошибок целостности данных. INTEGRITYERROR будет вызвано, если ваша база данных обеспечивает ссылочную целостность.

ЗАКЛЮЧЕНИЕ

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

Код доступен здесь, если вы хотите играть.

Создание модели, отношения ForeignKey и администратор Django

В этой части руководства TaskBuster мы продолжим определение наших моделей. Мы определим модели Project и Tag, которые имеют отношение External Key к модели Profile.

Кроме того, мы поговорим о пользовательской проверке , тестировании и настройке Admin Site с помощью встроенных моделей .

Контур этой части:

Начнем! 🙂

Версия схемы UML

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

Давайте посмотрим на UML-диаграмму наших моделей:

Резюме:

  • Модель профиля имеет отношение OneToOne с моделью пользователя.
  • И модель проекта, и модель тега имеют отношение ForeignKey с моделью профиля,
  • , а модель Task имеет
    • отношения ForeignKey с моделью проекта
    • Связь ManyToMany с моделью тега
    • Самостоятельное отношение ForeignKey к самому себе. # [0-9a-fA-F] {6} $)»)],
      verbose_name = _ ("цвет"),
      help_text = _ ("Введите шестнадцатеричный код цвета, например #ccc или #cccccc")
      )
      # Атрибуты - необязательно
      # Диспетчер объектов
      объекты = менеджеры.Руководитель проекта()
      # Настраиваемые свойства
      # Методы

      # Мета и строка
      класс Мета:
      verbose_name = _ ("Проект")
      verbose_name_plural = _ ("Проекты")
      ordering = ("пользователь", "имя")
      unique_to together = ("пользователь", "имя")

      def __str __ (сам):
      вернуть "% s -% s"% (self.user, self.name)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      140002

      14

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      000

      000 34

      35

      36

      37

      38

      39

      из django.core.validators import RegexValidator

      class Project (models.Model):

      # Relations

      user = models.ForeignKey (

      Profile,

      related_name = "projects",

      _ verbose user ")

      )

      # Атрибуты - обязательно

      name = models.CharField (

      max_length = 100,

      verbose_name = _ (" name "),

      help_text = _ (" Введите название проекта ")

      )

      цвет = модели.# [0-9a-fA-F] {6} $) ")],

      verbose_name = _ (" color "),

      help_text = _ (" Введите шестнадцатеричный код цвета, например #ccc или #cccccc " )

      )

      # Атрибуты - Необязательно

      # Диспетчер объектов

      objects = manager.ProjectManager ()

      # Настраиваемые свойства

      # Методы

      # Meta и String

      class Meta ver: 9000se3

      _ ("Проект")

      verbose_name_plural = _ ("Проекты")

      ordering = ("пользователь", "имя")

      unique_topting = ("пользователь", "имя")

      def __str __ (self ):

      возврат "% s -% s"% (self.пользователь, self.name)

      и в taskbuster / apps / taskmanager / manager.py добавьте:

      класс ProjectManager (models.Manager):
      пройти

      класс ProjectManager (models.Manager):

      пройти

      Давайте шаг за шагом прочитаем этот код:

      • Модель проекта имеет отношение внешнего ключа к модели профиля.
        • Это означает, что:
          • каждый экземпляр проекта должен быть связан с одним профилем пользователя (поле профиля обязательно),
          • и каждый профиль пользователя может быть связан с нулем, одним или несколькими проектами.
        • Из экземпляра проекта с именем myproject мы можем получить связанный с ним профиль с помощью:
          myproject.user

          • да, обратите внимание, что имя атрибута, определенное в Project, - это пользователь, а не профиль.
        • Из экземпляра профиля с именем myprofile мы можем получить связанные с ним проекты с помощью:
          мой профиль.projects.all ().

          • Без указания related_name по умолчанию вы должны получить доступ к проектам профиля с помощью
            myprofile.project_set.all ().
          • Обратите внимание, что
            myprofile.project возвращает диспетчер объектов, поэтому, если мы хотим получить экземпляры проекта, нам нужно использовать некоторые из обычных методов запроса, такие как all (), filter (), exclude () и т. д. Мы могли бы даже вызвать настраиваемые методы, определенные в настраиваемом классе ProjectManager.
        • Как мы видели в предыдущей части, подробное имя просто указывает удобочитаемое имя этого атрибута.
          • Обратите внимание, что он использует функцию перевода ugettext_lazy (прочтите предыдущую часть, чтобы увидеть импорт).

      Отношения «один ко многим»: у каждого музыканта оркестра есть отношения «Иностранный ключ» со своим дирижером

      • Название проекта. Это атрибут CharField, максимальная длина которого составляет 100 символов.
        • help_text - это текст, который будет отображаться в модельных формах, чтобы пользователь знал, что ему следует написать.
      • Color - еще один атрибут CharField с максимальной длиной 7.
        • Мы ожидаем шестнадцатеричный цвет, который состоит из трех шестнадцатеричных чисел от 00 до FF, каждое из которых указывает уровень красного, зеленого и синего цветов. Вместе они образуют строку из 6 символов плюс #, например #XXXXXX.
          • Например, черный - # 000000, а белый - #FFFFFF.
          • Однако, когда три числа образованы парами одного и того же числа, например # 001122, каждое из них может быть сокращено одной цифрой, например # 012. Таким образом, черный цвет может быть записан как # 000, а белый - как #FFF.
          • По умолчанию это поле будет белого цвета.
        • Чтобы принимать только правильные значения цветов Hex, мы используем специальный валидатор. RegexValidator принимает только строки, соответствующие указанному регулярному выражению.
      • Мы включаем настраиваемый диспетчер объектов, определенный в manager.py, ProjectManager.
      • В Meta мы определяем:
        • удобочитаемое имя класса
        • порядок по умолчанию при запросе экземпляров проекта
        • г.
          unique_toght свойство, которое определяет на уровне базы данных, что для одного и того же профиля мы не можем написать два проекта с одинаковым именем.
      • Метод __str__ вызывается всякий раз, когда метод str () вызывается для объекта, например, на сайте администратора или при отображении объекта в шаблонах Django.

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

      $ python manage.py проверить
      диспетчер задач $ python manage.py makemigrations
      $ python manage.py перенести диспетчер задач

      $ python manage.py check

      $ python manage.py makemigrations taskmanager

      $ python manage.py миграция диспетчера задач

      Затем давайте напишем несколько тестов для этой модели.

      Испытания для проекта Модель

      При написании тестов для моделей Django я обычно сосредотачиваюсь только на настраиваемых атрибутах и ​​функциях, у которых есть некоторые свойства или поведение, отличные от Django по умолчанию.

      Например, я не собираюсь тестировать правильное поведение max_length в CharField, поскольку это встроенная функция, которая наверняка была достаточно протестирована разработчиками Django.

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

      В taskbuster / apps / taskmanager / tests.py добавьте следующий тест:

      из django.core.exceptions import ValidationError

      класс TestProjectModel (TestCase):

      def setUp (сам):
      Пользователь = get_user_model ()
      self.user = User.objects.create (
      username = "taskbuster", password = "django-tutorial")
      себя.profile = self.user.profile

      def tearDown (сам):
      self.user.delete ()

      def test_validation_color (сам):
      # В этом первом проекте используется значение по умолчанию, #fff
      проект = модели.Проект (
      user = self.profile,
      name = "TaskManager"
      )
      self.assertTrue (project.color == "#fff")
      # Проверка не должна вызывать ошибку
      project.full_clean ()

      # Хороший ввод цвета (без ошибок):
      для цвета в ["# 1cA", "# 1256aB"]:
      проект.color = цвет
      project.full_clean ()

      # Неправильный ввод цвета:
      для цвета в ["1cA", "1256aB", "# 1", "# 12", "# 1234",
      "# 12345", "# 1234567"]:
      с self.assertRaises (
      Ошибка проверки,
      msg = "% s не вызвал ошибку ValidationError"% цвет):
      project.color = цвет
      project.full_clean ()

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      140002

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      000

      000 34

      35

      36

      37

      из django.core.exceptions import ValidationError

      class TestProjectModel (TestCase):

      def setUp (self):

      User = get_user_model ()

      self.user2 = User.objects "taskbuster", password = "django-tutorial")

      self.profile = self.user.profile

      def tearDown (self):

      self.user.delete ()

      def test_validation_color (self ):

      # В этом первом проекте используется значение по умолчанию, #fff

      project = models.Проект (

      user = self.profile,

      name = "TaskManager"

      )

      self.assertTrue (project.color == "#fff")

      # Проверка не должна вызывать ошибку проекта

      . full_clean ()

      # Хорошие входы цвета (без ошибок):

      для цвета в ["# 1cA", "# 1256aB"]:

      project.color = color

      project.full_clean ()

      # Неверный ввод цвета:

      для цвета в ["1cA", "1256aB", "# 1", "# 12", "# 1234",

      "# 12345", "# 1234567"]:

      с собой.assertRaises (

      ValidationError,

      msg = "% s не вызвал ошибку ValidationError"% color):

      project.color = color

      project.full_clean ()

      И давайте объясним, что он делает:

      • The
        setUp выполняется в начале каждого теста:

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

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

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

      Хорошо! теперь, когда мы понимаем этот тест, вы можете запустить его с помощью:

      $ python manage.py тест taskbuster.apps.taskmanager.tests.TestProjectModel

      $ python manage.py test taskbuster.apps.taskmanager.tests.TestProjectModel

      Надеюсь, это сработало! 🙂

      Администратор Django для модели проекта: отображение настраиваемого списка и встроенная модель

      Теперь, когда наша модель определена и протестирована, мы можем добавить ее на сайт администратора.

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

      В taskbuster / apps / taskmanager / admin.py замените ProfileAdmin на:

      # - * - кодировка: utf-8 - * -
      от администратора импорта django.contrib
      из . импортные модели

      класс ProjectsInLine (admin.TabularInline):
      model = models.Project
      extra = 0

      @ admin.register (модели.Профиль)
      класс ProfileAdmin (admin.ModelAdmin):

      list_display = ("имя пользователя", "взаимодействие", "_projects")

      search_fields = ["user__username"]

      inlines = [
      Проекты
      ]

      def _projects (self, obj):
      вернуть объект.projects.all (). count ()

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      140002

      18

      19

      20

      21

      22

      23

      # - * - кодировка: utf-8 - * -

      из django.contrib import admin

      из. импорт моделей

      класс ProjectsInLine (admin.TabularInline):

      model = models.Project

      extra = 0

      @ admin.register (models.Profile)

      . ModelAdmin):

      list_display = ("имя пользователя", "взаимодействие", "_projects")

      search_fields = ["user__username"]

      inlines = [

      Projects

      0002]

      def _projects (self, obj):

      return obj.projects.all (). count ()

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

      $ python управлять сервером запуска

      $ управляющий сервер запуска python

      и перейдем в список профиля администратора. Мы обсудим предыдущий код шаг за шагом через минуту.

      Сначала отредактируйте (или создайте, если у вас его нет) экземпляр профиля.Вы увидите что-то похожее на:

      Обратите внимание, что внизу я создал два разных проекта: Blog и TaskBuster. Но подождите, почему они здесь появляются?

      Это благодаря определению класса ProjectsInLine, который наследуется от Django TabularInLine:

      класс ProjectsInLine (admin.TabularInline):
      model = models.Project
      extra = 0

      класс ProjectsInLine (админ.TabularInline):

      model = models.Project

      extra = 0

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

      Более того, связь между ProjectsInLine и ProfileAdmin осуществляется здесь:

      @ admin.register (модели.Профиль)
      класс ProfileAdmin (admin.ModelAdmin):
      ...
      inlines = [
      Проекты
      ]
      ...

      @ admin.register (models.Profile)

      class ProfileAdmin (admin.ModelAdmin):

      ...

      inlines = [

      ProjectsInLine

      ]

      ...

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

      С другой стороны, если мы вернемся к списку профилей, мы увидим что-то вроде:

      Что показывает список экземпляров профиля с указанием имени пользователя, значения взаимодействия и… сколько у него проектов?

      Вы думали, что здесь можно показать только атрибуты модели? Ну, по-видимому, вы также можете определить пользовательские функции 🙂

      Давайте посмотрим на свойство list_display:

      list_display = ("имя пользователя", "взаимодействие", "_projects")

      list_display = ("имя пользователя", "взаимодействие", "_projects")

      он содержит имя пользователя, взаимодействие и другое свойство _projects, которое не является атрибутом модели или настраиваемым свойством.Однако вы увидите собственный метод, определенный внутри ProfileAdmin:

      def _projects (self, obj):
      вернуть obj.projects.all (). count ()

      def _projects (self, obj):

      return obj.projects.all (). Count ()

      Он принимает два аргумента: self (экземпляр ProfileAdmin) и obj (экземпляр Profile, который мы редактируем).

      Таким образом, этот метод просто запрашивает все проекты, связанные с экземпляром профиля, и подсчитывает их.Большой! 🙂

      Модель тега: еще одна простая модель с отношением ForeignKey

      Как видно на диаграмме UML, модель тега очень похожа на модель проекта:

      • Он имеет отношение ForeignKey с моделью профиля
      • Имеет свойство имени

      Поскольку у него нет дополнительных функций, мы можем определить его прямо в наших файлах models.py и manager.py:

      # models.py

      class Tag (models.Модель):
      # Связи
      user = models.ForeignKey (
      Профиль,
      related_name = "теги",
      verbose_name = _ ("пользователь")
      )
      # Атрибуты - обязательно
      name = models.CharField (
      max_length = 100,
      verbose_name = _ ("Имя")
      )
      # Атрибуты - необязательно
      # Диспетчер объектов
      объекты = менеджеры.TagManager ()
      # Настраиваемые свойства
      # Методы

      # Мета и строка
      класс Мета:
      verbose_name = _ ("Тег")
      verbose_name_plural = _ ("Теги")
      ordering = ("пользователь", "имя",)
      unique_to together = ("пользователь", "имя")

      def __str __ (сам):
      return "% s -% s"% (self.пользователь, self.name)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      140002

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      # модели.py

      class Tag (models.Model):

      # Relations

      user = models.ForeignKey (

      Profile,

      related_name = "tags",

      verbose_name = _ ("user")

      )

      # Атрибуты - Обязательно

      name = models.CharField (

      max_length = 100,

      verbose_name = _ ("Имя")

      )

      # Атрибуты - Необязательно

      # Диспетчер объектов

      объектов = менеджеры.TagManager ()

      # Пользовательские свойства

      # Методы

      # Мета и строка

      class Meta:

      verbose_name = _ ("Tag")

      verbose_name_plural = _ ("Tags")

      (порядок тегов)

      "пользователь", "имя",)

      unique_to together = ("пользователь", "имя")

      def __str __ (self):

      return "% s -% s"% (self.user, self. имя)

      # менеджеров.ру

      класс TagManager (models.Manager):
      пройти

      # manager.py

      class TagManager (models.Manager):

      pass

      И отредактируйте файл admin.py, чтобы добавить встроенную модель тега:

      # admin.py

      # - * - кодировка: utf-8 - * -
      от администратора импорта django.contrib
      из . импортные модели

      класс ProjectsInLine (admin.TabularInline):
      model = models.Project
      extra = 0

      класс TagsInLine (admin.TabularInline):
      model = models.Tag
      extra = 0

      @ admin.register (модели.Профиль)
      класс ProfileAdmin (admin.ModelAdmin):

      list_display = ("имя пользователя", "взаимодействие", "_projects", "_tags")

      search_fields = ["user__username"]

      inlines = [
      ПроектыInLine, ТегиInLine
      ]

      def _projects (self, obj):
      вернуть объект.projects.all (). count ()

      def _tags (self, obj):
      вернуть obj.tags.all (). count ()

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      140002

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      0003

      # admin.py

      # - * - кодировка: utf-8 - * -

      из django.contrib import admin

      from. импорт моделей

      class ProjectsInLine (admin.TabularInline):

      model = models.Project

      extra = 0

      class TagsInLine (.TabularInline):

      admin model =

      admin model.

      extra = 0

      @ admin.register (models.Profile)

      class ProfileAdmin (admin.ModelAdmin):

      list_display = ("имя пользователя", "взаимодействие", "_projects", "_tags")

      search_fields = ["user__username"]

      inlines = [

      TagsInlines]

      ]

      def _projects (self, obj):

      return obj.projects.all (). Count ()

      def _tags (self, obj):

      return obj.tags.all () .count ()

      Большой! Теперь перенесите вашу базу данных:

      $ python manage.ру чек
      диспетчер задач $ python manage.py makemigrations
      $ python manage.py перенести диспетчер задач

      $ python manage.py check

      $ python manage.py makemigrations taskmanager

      $ python manage.py migrate taskmanager

      результаты проверяем в админке:

      $ python manage.py runserver

      $ python manage.py runserver

      Снова запустите тесты и убедитесь, что ничего не сломано! 🙂

      и, наконец, зафиксируйте свои изменения:

      $ git status
      $ git add.
      $ git commit -m "Конец части X"

      $ git status

      $ git add.

      $ git commit -m "Конец части X"

      На сегодня все!

      Пожалуйста, поставьте +1, если пригодится! Спасибо!

      Понимание «многие к одному» в Django

      Повторное введение в одну из наиболее распространенных взаимосвязей между базами данных: понимание «многие к одному».

      Что такое отношение «многие к одному»?

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

      Приведем пример. Рассмотрим две сущности: Мать и Дочь . Как мы знаем из реальной жизни, эти две сущности в большинстве случаев связаны следующим образом:

      • Каждые Дочерние имеют одна Мать
      • Одна мать может иметь много дочерей

      Итак, у нас есть многие к одному , где многие - это Дочь, а - одна - Мать.

      В диаграмме взаимосвязи сущностей это отношение описывается двумя прямоугольниками, соединенными популярной формулой "гусиная лапка" :

      .

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

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

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

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

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

      Отношение "многие к одному" на практике

      Чтобы связать две сущности в связи многие к одному , нам нужны как минимум две таблицы в базе данных . Для примера Mother у нас будет материнская таблица (примеры показаны на диалекте PostgreSQL ):

        СОЗДАТЬ ТАБЛИЦУ мать (
          id smallserial NOT NULL PRIMARY KEY,
          first_name varchar (254) НЕ NULL,
          last_name varchar (254) НЕ ПУСТО
      );  

      А как насчет Дочь ? Это сторона «многих».Конечно, мы можем создать другую таблицу, как мы сделали с матерью:

        СОЗДАТЬ ТАБЛИЦУ дочь (
          id smallserial NOT NULL PRIMARY KEY,
          first_name varchar (254) НЕ NULL,
          last_name varchar (254) НЕ ПУСТО
      );  

      Но теперь, как мы должны соединить две таблицы? Это делается с помощью внешнего ключа . Внешний ключ - это то, что говорит название: это ключ, указывающий из одной таблицы в другую (внешнюю). Перед добавлением давайте отбросим таблицу и создадим новую со столбцом с именем mother_id :

        DROP TABLE дочь;
      
      СОЗДАТЬ ТАБЛИЦУ дочь (
          id smallserial NOT NULL PRIMARY KEY,
          first_name varchar (254) НЕ NULL,
          last_name varchar (254) НЕ NULL,
          mother_id целое НЕ NULL
      );  

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

        ALTER TABLE дочь
      ДОБАВИТЬ ОГРАНИЧЕНИЕ fk_mother_id
      ИНОСТРАННЫЙ КЛЮЧ (mother_id)
      ССЫЛКИ мать (id);  

      С помощью этого запроса мы говорим базе данных: добавьте новый внешний ключ в столбец mother_id, чтобы он ссылался на столбец id в таблице с именем mother .

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

      Понимание многих к одному в Django

      Вам нужно будет создать новый проект Django и приложение Django с именем address_book, прежде чем тестировать .

      Чтобы проиллюстрировать «многие к одному» в Django, давайте сделаем еще один пример. Рассмотрим две сущности: Пользователь и Контакт . Для этих сущностей мы говорим, что:

      • Каждый Контакт имеет одного пользователя
      • У одного пользователя может быть много контактов

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

      joel89 имеет abc @ abc.dev, [email protected], [email protected] в своей адресной книге

      jules84 имеет [email protected], [email protected], [email protected] в своей адресной книге

      и так далее. В переводе на диаграмму взаимоотношений сущностей это становится:

      Теперь для создания этих сущностей (моделей) в приложении Django мы можем использовать django.db.models . Создаем две модели, каждая с соответствующими полями:

        из моделей импорта django.db
      
      
      класс User (models.Model):
          имя пользователя = модели.CharField (max_length = 150)
      
      
      класс Контакт (models.Model):
          email = models.EmailField ()  

      Затем после регистрации приложения в настройках Django мы запускаем python manage.py makemigrations и python manage.py migrate для создания новых таблиц в базе данных.

      При миграции будут созданы две таблицы, но еще не связанные. Пора добавить внешний ключ !

      Понимание многих к одному в Django: внешние ключи

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

        из моделей импорта django.db
      
      
      класс User (models.Model):
          имя пользователя = models.CharField (max_length = 150)
      
      
      класс Контакт (models.Model):
          email = models.EmailField ()
          user = models.ForeignKey (to = User, on_delete = models.CASCADE)  

      Здесь мы добавляем новый столбец с именем user, ссылающийся на модель User с ForeignKey . Обязательно запустите python manage.py makemigrations и python manage.py migrate , чтобы применить изменения.

      Важно отметить, что ForeignKey принимает как минимум два аргумента:

        user = models.ForeignKey (to = User, on_delete = models.CASCADE)  

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

      Также следует отметить, что Django работает несколько иначе, чем другие фреймворки. Некоторые фреймворки позволяют вам иметь отношение «один ко многим», где сторона «многие» может быть определена в «одном». Так обстоит дело, например, с Laravel one-to-many.

      В итоге результат один: таблица "многие" всегда будет связана с внешним ключом , - с "единицей".

      Теперь мы готовы к делать запросы с Django ORM .

      Для проверки войдите в консоль Django:

      Затем импортируйте две модели, Пользователь и Контакт:

        >>> из адресной_книги.импорт моделей Пользователь, Контакт  

      Теперь заполните базу данных пользователем:

        >>> jules84 = User.objects.create (username = "jules84")  

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

        >>> Contact.objects.create (email = "[email protected]", user = jules84)
      >>> Contact.objects.create (email = "[email protected]", user = jules84)
      >>> Contact.objects.create (email = "[email protected]", user = jules84)  

      При создании нового контакта это работает так: передав User в качестве аргумента для create , мы связываем две сущности вместе .Имея эти сущности, мы теперь готовы к делать запросы .

      Чтобы получить доступ к пользователю из нашей базы данных, мы можем запустить:

        >>> User.objects.get (username = "jules84")  

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

      А теперь, как насчет получить каждый контакт, связанный с этим пользователем ? С Django ORM мы можем использовать так называемый поиск , который имеет форму .related_set , где related - это имя объекта, к которому мы хотим получить доступ.

      Итак, чтобы получить все контакты, подключенные к нашему пользователю (у пользователя много контактов), мы можем запустить:

        >>> User.objects.get (username = "jules84"). Contact_set.all ()  

      Этот запрос возвращает:

        , <Контакт: [email protected]>, <Контакт: [email protected]>]>  

      Есть также способ сделать обратное: из контакта мы можем вернуться к соответствующему пользователю (у контакта есть один пользователь):

        >>> Контакты.objects.first (). пользователь  

      Этот запрос возвращает соответствующего пользователя для нашего контакта:

      Ресурсы

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

      Многие к одному в Django

      Наблюдение за изменениями ForeignKey - хуки жизненного цикла Django

      Изменения в ссылке ForeignKey

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

        класс Организация (мод.Модель):
          name = models.CharField (max_length = 100)
      
      
      класс UserAccount (LifecycleModel):
          имя пользователя = models.CharField (max_length = 100)
          email = models.CharField (max_length = 600)
          работодатель = models.ForeignKey (Организация, on_delete = models.SET_NULL)
      
          @hook (AFTER_UPDATE, when = "работодатель", has_changed = True)
          def notify_user_of_employer_change (самостоятельно):
              mail.send_mail («Обновить», «Теперь вы работаете на кого-то другого!», [self.email])
        

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

      Изменения значения поля ForeignKey

      У вас может быть задействованный метод срабатывания на основе значения поля в модели, связанной с внешним ключом, с использованием точечной нотации:

        класс Организация (модели. Модель):
          name = models.CharField (max_length = 100)
      
      
      класс UserAccount (LifecycleModel):
          имя пользователя = models.CharField (max_length = 100)
          email = models.CharField (max_length = 600)
          работодатель = модели.ForeignKey (Организация, on_delete = models.SET_NULL)
      
          @hook (AFTER_UPDATE, when = "работодатель.имя", has_changed = True, is_now = "Google")
          def notify_user_of_google_buy_out (самостоятельно):
              mail.send_mail ("Обновить", "Google купил вашего работодателя!", ["[email protected]"],)
        

      Если вы используете точечную нотацию , Обратите внимание на потенциальное снижение производительности. : При первой инициализации вашей модели соответствующая модель также будет загружена для сохранения «начального» состояния связанного поля.Модели, настроенные с помощью этих хуков, всегда следует загружать с использованием .select_related () , то есть UserAccount.objects.select_related ("organization") для приведенного выше примера. Если вы этого не сделаете, вы почти наверняка столкнетесь с серьезной проблемой производительности N + 1.

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

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