Джанго Дженерик Фабрика - PullRequest
0 голосов
/ 12 июня 2018

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

from django.contrib.gis.db import models


class Country(models.Model):
    name = models.CharField(max_length=60)


class Region(models.Model):
    country = models.ForeignKey(Country, on_delete=models.PROTECT)
    name = models.CharField(max_length=60)


class Law(models.Model):
    text = models.CharField(max_length=60)

    class Meta:
        abstract = True


class CountryLaw(Law):
    country = models.ForeignKey(Country, on_delete=models.CASCADE)


class RegionLaw(Law):
    region = models.ForeignKey(Region, on_delete=models.CASCADE)

# Not sure this work but the idea is here
class LawManager(models.Manager):
    def create_law(text,geodata):
        if isinstance(geodata, Country):
            return CountryLaw(text=text, country=geodata)
        elif isinstance(geodata, Region)
            return RegionLaw(text=text, region=geodata)
        else:
            raise TypeError("Inapropriate geodata type")

Я хотел бы какой-то фабричный метод, потому что у меня есть некоторая работа, чтобы заполнить поля "Закона", который является общим для всех законов, но этот примерне показывает это.У меня такой вопрос:

  • Есть ли лучший способ проектирования объектов Law?
  • Будет ли работать такой менеджер?Как я могу получить к нему доступ?

Я ищу в google и stackoverflow ответ, но не знаю, какое ключевое слово использовать, и не нашел ничего, что могло бы мне помочь.

спасибоза вашу помощь!

1 Ответ

0 голосов
/ 13 июня 2018

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

Оставляя модели такими, как это

В этом случае мытаким образом смоделируйте это как:

+---------+ 1      N +------------+
| Country |----------| CountryLaw |
+---------+          +------------+
    | 1
    |
    | N
+---------+ 1      N +-----------+
| Region  |----------| RegionLaw |
+---------+          +-----------+

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

Преимущество состоит в том, что, если два имеют определенную семантику, например, CountryLaw должен обрабатываться совершенно по-разномуот RegionLaw, тогда это легче реализовать.Кроме того, если у CountryLaw есть определенные поля, которые, например, не важны для RegionLaw s (или наоборот), тогда мы не будем тратить дисковое пространство на значения NULL (или другие заполнители).

Недостатком является то, что если мы, например, хотим запросить законы, которые являются частью 'Germany', мы должны сделать это в два этапа: запросить CountryLaw s Германии и запросить RegionLaw s всех регионовGermany.Это также может легко выйти из-под контроля, если у вас также есть SubRegion s, City s и т. Д.

Работа с Прокси регионами

Здесь мы рассмотрим всеLaw s для определенного Region, но хитрость в том, что мы создаем Region, который действует как вся страна.Таким образом, кроме 'Saxony' и 'Bavaria', мы используем виртуальный регион 'Germany', который будет представлять всю страну.

Затем мы можем ввести одну модель Law, то естьприкреплен к Region.Если нам часто требуется проводить различие между регионом и страной, мы можем добавить поле is_country, которое, например, указывает, является ли это «прокси-сервером страны» или реальным регионом:

+---------+
| Country |
+---------+
    | 1
    |
    | N
+-------------------+ 1      N +-----+
|       Region      |----------| Law |
+-------------------+          +-----+
| is_country : bool |
+-------------------+

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

Недостатком является то, что если страна Law s и регион Law s значительно отличаются, то это приведет кв огромном количестве проверок (каждый раз проверяя, является ли присоединенный регион действительно регионом или страной), и, кроме того, это может привести к множеству неиспользуемых полей.Кроме того, если мы включаем все больше и больше слоев, нам нужно вводить все больше и больше прокси-объектов.Если бы мы, например, использовали бы три слоя (Country, Region, SubRegion), то нам нужно построить для каждой страны «прокси» регион (который также содержит субрегион proxy ) и для каждого региона субрегион proxy .Таким образом, если существует n стран и m регионов, это приведет к 2 × n + m прокси-объектам, что также приведет к дублированию данных (мы повторяем название страны / региона несколько раз, и если впоследствии страна или регион будут переименованы, это приведет к некоторой боли при обновлении всех этих прокси).

Работа с древовидная -подобная структура

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

Здесь мы определяем модельНапример, Area, Area может иметь parent, что также является Area, мы можем построить древовидную -структуру таким образом.Например, что-то вроде:

class <b>Area</b>(models.Model):
    parent = ForeignKey('app.Area', on_delete=models.SET_NULL, related_name='children')

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

  ----
 N|  |1
+------+ 1      N +-----+
| Area |----------| Law |
+------+          +-----+

Преимущество в том, что здесь у нас есть одна модель для Country, Region, Subregion и т. Д. Кроме того, мы можем реализоватьиерархия в точности так, как мы хотим, где, например, в некоторых (малых) странах нет Region с (например, " города-государства ", например Ватикан , Сингапур , так далее.).Кроме того, объект Law будет ссылаться на объект, подобный области.Также легко получить законы, прикрепленные к определенной области.

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

ИспользованиеGenericForeignKey в Law объекте (функция Django)

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

По умолчанию Django добавляет неявный первичный ключ к каждой модели:если разработчик не , укажите поле с primary_key=True.Django автоматически добавит IntegerField, который будет указывать идентификатор в качестве первичного ключа.Таким образом, разумно предположить, что большинство моделей имеют IntegerField в качестве первичного ключа (фактически это просто un-Django для указания другого первичного ключа).Мы также можем сгенерировать список, который отображает каждую модель в целое число: мы можем, например, сказать, что 0 отображается на User, 1 отображается на Group и т. Д.

Это означает, что большинствоэкземпляры модели могут быть идентифицированы двумя целыми числами: одно целое число, которое определяет модель, и одно, которое указывает первичный ключ соответствующей модели.Например, (0, 14) - это User с первичным ключом 14 (учитывая, что мы используем «справочную таблицу», определенную в параграфе выше).Это мощная концепция: таким образом, мы можем использовать два столбца базы данных для хранения этих целых чисел и каждый раз позволять Django извлекать объект.Это идея GenericForeignKey.Таким образом, мы можем определить Law как:

class Law(models.Model):
    <b>content_type</b> = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    <b>object_id</b> = models.PositiveIntegerField()
    <b>area = GenericForeignKey('content_type', 'object_id')</b>

Так что это означает, что теперь наш Law хранит два действительных поля: content_type и object_id, и если мы запрашиваем some_law.area, Django извлечет объект, который соответствует этим двум целым числам.

Таким образом, мы можем использовать это для ссылки на Country, Region, Subregion, что может быть полезно в случае, если мы можем ссылатьсяк множеству моделей.Но есть много недостатков.Основная проблема заключается в том, что такие отношения обычно очень громоздки в том случае, если мы хотим использовать их в запросе.Действительно: скажем, мы хотим объединить нашу модель Law с полем area.Тогда к какой таблице мы должны присоединиться?Country, Region, SubRegion?Что если один Law относится к Country, тогда как другой относится к Region?Поэтому, как правило, мы не можем JOIN.

Кроме того, сама модель делает не гарантией того, что GenericForeignKey всегда будет ссылаться на объект, подобный области,Это может относиться к другому Law, User, Criminal и т. Д. Таким образом, в результате вы несете ответственность за написание вменяемой логики, которая всегда будет гарантировать, что отношения имеют смысл.Хотя это выглядит легко, может быть трудно поддерживать хорошие вменяемые отношения.Большинство баз данных не могут проверить, ссылается ли внешний ключ на допустимый объект, поскольку нет ограничения FOREIGN KEY, поскольку "таблица назначения" неизвестна.

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

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

+---------+
| Country |
+---------+.
    | 1      .
    |          .
    | N          .
+---------+        . +-----+
| Region  |. . . . . | Law |
+---------+ 1      N +-----+
...