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

    image

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

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

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

    _____Отношения между моделями
    _______Отношение многие-к-одному
    _______Отношение многие-к-многим
    _______Дополнительные поля в отношении многие-к-многим
    _______Отношение один-к-одному
    _____Модели и файлы
    _____Ограничения на имена полей
    _____Собственные типы полей


    Перевод Django Documentation: Models. Part 3
    Перевод Django Documentation: Models. Part 4 (Last)



    Отношения между моделями


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



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

    Для определения данного вида отношений используется ForeignKey. Его использование ничем не отличается от использования других типов полей: просто добавьте соответствующий атрибут в вашу модель.

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

    Например, если машина (Car) имеет производителя (Manufacturer) (производитель создает большое количество машин, однако каждая машина имеет лишь одного производителя), используется следующее определение:
    Copy Source | Copy HTML<br/>class Manufacturer(models.Model):<br/>    # ...<br/> <br/>class Car(models.Model):<br/>    manufacturer = models.ForeignKey(Manufacturer)<br/>    # ... <br/>

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

    В качестве имени поля типа ForeignKey (manufacturer в примере, приведенном выше) мы советуем использовать имя модели, на которую ссылается ваш класс, в нижнем регистре. Это необязательное требование, и вы можете называть поле так, как хотите. Например:
    Copy Source | Copy HTML<br/>class Car(models.Model):<br/>    company_that_makes_it = models.ForeignKey(Manufacturer)<br/>    # ... <br/>


    image
    Более подробный пример вы можете найти здесь — отношение многие-к-одному: пример

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



    Отношение многие к многим

    Для определения данного вида отношений используется ManyToManyField. Его использование ничем не отличается от использования других типов полей: просто добавьте соответствующий атрибут в вашу модель.

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

    Например, если пицца (Pizza) имеет начинку (Topping) (одна пицца может иметь множество начинок, которые в свою очередь могут содержаться во многих пиццах), вы можете представить это так:
    Copy Source | Copy HTML<br/>class Topping(models.Model):<br/>    # ...<br/> <br/>class Pizza(models.Model):<br/>    # ...<br/>    toppings = models.ManyToManyField(Topping) <br/>

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

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

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

    Как правило, поле типа ManyToManyField определяется в объекте, который впоследствии будет изменяться в интерфейсе администратора, если вы используете встроенную админку Django (прим.пер. извините, или сленг или тавтология). В примере выше начинки (toppings) находятся на пицце (Pizza), потому что более привычно для нас представлять себе пиццу, имеющую разные начинки, нежели чем начинку, содержащуюся на множестве пицц. Если описать модель так, как показано выше, форма Pizza позволит пользователям выбирать начинки.

    image
    Более подробный пример вы можете найти здесь — отношение многие-к-многим: пример

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



    Дополнительные поля в отношении многие-к-многим
    добавлено в версии Django 1.0: пожалуйста, прочтите примечания к релизу.

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

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

    Для таких ситуаций Django предоставляет вам возможность определить отдельную (промежуточную) модель, которая будет использоваться для управление отношениями многие-к-многим. Вы можете создать дополнительные поля в промежуточной модели. Промежуточная модель взаимодействует с ManyToManyField с помощью аргумента through, который выступает в качестве посредника. Для нашего музыкального (:)) примера код будет выглядеть примерно так:
    Copy Source | Copy HTML<br/>class Person(models.Model):<br/>    name = models.CharField(max_length=128)<br/> <br/>    def __unicode__(self):<br/>        return self.name<br/> <br/>class Group(models.Model):<br/>    name = models.CharField(max_length=128)<br/>    members = models.ManyToManyField(Person, through='Membership')<br/> <br/>    def __unicode__(self):<br/>        return self.name<br/> <br/>class Membership(models.Model):<br/>    person = models.ForeignKey(Person)<br/>    group = models.ForeignKey(Group)<br/>    date_joined = models.DateField()<br/>    invite_reason = models.CharField(max_length=64) <br/>

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

    Существует несколько ограничений, касающихся промежуточных моделей:
    • Ваша промежуточная модель должна содержать один и только один внешний ключ к целевой модели (в нашем примере это Person), иначе будет сгенерирована ошибка проверки (validation error).
    • Ваша промежуточная модель должна содержать один и только один внешний ключ к исходной модели (в нашем примере это Group), иначе будет сгенерирована ошибка проверки (validation error).
    • Единственным исключением является модель, которая имеет отношение многие-к-многим сама с собой и ссылается на себя с помощью промежуточной модели. В этом случае разрешается использовать два внешних ключа к одной и той же модели, но они буду рассматриваться как две различные части в отношениях вида многие-к-многим.
    • Для определения модели, которая ссылается сама на себя с помощью промежуточной модели, вы должны использовать symmetrical=False (подробнее в справке по полям моделей).

      Теперь, после того как вы настроили ManyToManyField для взаимодействия с вашей промежуточной моделью, вы можете начать создание нескольких отношений вида многие-к-многим. Делается это путем создания экземпляров вашей промежуточной модели:
      Copy Source | Copy HTML<br/>>>> ringo = Person.objects.create(name="Ringo Starr")<br/>>>> paul = Person.objects.create(name="Paul McCartney")<br/>>>> beatles = Group.objects.create(name="The Beatles")<br/>>>> m1 = Membership(person=ringo, group=beatles,<br/>... date_joined=date(1962, 8, 16),<br/>... invite_reason= "Needed a new drummer.")<br/>>>> m1.save()<br/>>>> beatles.members.all()<br/>[<Person: Ringo Starr>]<br/>>>> ringo.group_set.all()<br/>[<Group: The Beatles>]<br/>>>> m2 = Membership.objects.create(person=paul, group=beatles,<br/>... date_joined=date(1960, 8, 1),<br/>... invite_reason= "Wanted to form a band.")<br/>>>> beatles.members.all()<br/>[<Person: Ringo Starr>, <Person: Paul McCartney>] <br/>

      Не очень похоже на обычные поля: вы не можете использовать методы add и create, а также операцию присваивания (например, beatles.members = [...]) для определения отношений:
      Copy Source | Copy HTML<br/># THIS WILL NOT WORK<br/>>>> beatles.members.add(john)<br/># NEITHER WILL THIS<br/>>>> beatles.members.create(name="George Harrison")<br/># AND NEITHER WILL THIS<br/>>>> beatles.members = [john, paul, ringo, george] <br/>

      Почему? Вы не можете просто создать отношение между Person и Group, поэтому вы должны указать все детали отношения, требуемые промежуточной моделью Membership. Простые методы add, create и присваивание не предоставляют возможности определить дополнительные детали. Поэтому они отключены в отношения вида многие-к-многим с использованием промежуточной модели. Единственный вариант создания данного вида отношений, заключается в создании экземпляров промежуточной модели.

      Метод remove отключен по этим же причинам. Однако вы можете использовать метод clear() для удаления всех отношений вида многие-к-многим отношений экземпляра:
      Copy Source | Copy HTML<br/># Beatles have broken up<br/>>>> beatles.members.clear() <br/>

      Итак, вы создали отношения вида многие-к-многим путем создания экземпляров промежуточной модели, теперь вы можете оформлять запросы. Так же как и и в случае с нормальным вариантом отношений, вы можете составлять запросы, используя атрибуты модели:
      Copy Source | Copy HTML<br/># Find all the groups with a member whose name starts with 'Paul'<br/>>>> Group.objects.filter(members__name__startswith='Paul')<br/>[<Group: The Beatles>] <br/>

      Так как вы используете промежуточную модель, вы также можете оформлять запросы, используя атрибуты модели-посредника:
      Copy Source | Copy HTML<br/># Find all the members of the Beatles that joined after 1 Jan 1961<br/>>>> Person.objects.filter(<br/>... group__name='The Beatles',<br/>... membership__date_joined__gt=date(1961,1,1))<br/>[<Person: Ringo Starr] <br/>




      Отношение один-к-одному

      Для определения данного вида отношений используется OneToOneField. Его использование ничем не отличается от использования других типов полей: просто добавьте соответствующий атрибут в вашу модель.

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

      При создании поля с использованием OneToOneField, одноименному методу следует передать один позиционный аргумент: класс, на который будет ссылаться ваша модель.

      Например, если вы решили создать базу данных «Places», вы, скорее всего, добавите в нее совершенно стандартные вещи, такие как адрес, телефон и так далее. Если же вы захотите создать новую базу данных о ресторанах, которые располагаются в местах из первой базы данных, вместо повторения и дублирования информации в полях модели Restaurant, вы можете установить отношение один-к-одному между Restaurant и Place с помощью OneToOneField (потому что рестаран, фактически, является местом; чтобы справится с этой задачей вам придется использовать наследование, которое неявно включает в себя вид отношений один-к-одному).

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

      image
      Более подробный пример вы можете найти здесь — отношение один-к-одному: пример

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

      Ранее классы OneToOneField автоматически становились первичными ключами модели. Это больше не так (хотя возможность установки primary_key вручную осталась). Таким образом, теперь возможно иметь несколько полей типа OneToOneField в одной модели.



      Модели и файлы


      Вполне возможно связать две модели из разных приложений. Для это следует импортировать нужную модель в верхнюю часть нашей модели, а потом просто сослаться на другой класс, если это будет нужно. Например:
      Copy Source | Copy HTML<br/>from mysite.geography.models import ZipCode<br/> <br/>class Restaurant(models.Model):<br/>    # ...<br/>    zip_code = models.ForeignKey(ZipCode) <br/>




      Ограничения на имена полей


      Django устанавливает два ограничения на имена полей:
      1. Имя поля не может быть зарезервированным словом языка Python, иначе это вызовет синтаксическую ошибку. Например:
        Copy Source | Copy HTML<br/>class Example(models.Model):<br/>    pass = models.IntegerField() # 'pass' is a reserved word! <br/>
      2. Имя поля не может содержать более одного символа подчеркивания подряд. Это является следствием системы поиска запросов в Django. Например:
        Copy Source | Copy HTML<br/>class Example(models.Model):<br/>    foo__bar = models.IntegerField() # 'foo__bar' has two underscores! <br/>


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

      Зарезервированные слова SQL, такие как join, where или select могут быть использованы в качестве имен полей, в связи с тем, что Django «покидает» все столбцы и колонки базы данных при каждом SQL-запросе. В механизмах базы данных используется ссылочный синтаксис.



      Собственные типы полей

      добавлено в версии Django 1.0: пожалуйста, прочтите примечания к релизу.

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



      На сегодня все :) to be continued
      С радостью выслушаю любые ваши предложения, вопросы и замечания.

      Перевод Django Documentation: Models. Part 1
      Перевод Django Documentation: Models. Part 3
      Перевод Django Documentation: Models. Part 4 (Last)

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 58

    • UFO just landed and posted this here
        +4
        Ну пускай, человек благое дело делает. Главное, что все не закончилось на первой части.
        • UFO just landed and posted this here
            +1
            djbook.ru/
            Уже давно переводят, только оригинальная джангобук что-то давно не обновляется…
          +5
          Так и хочется крикнуть «Идиот!»
          Я искренне благодарен автору этого топика за то что он делает. Вы очень, очень не правы. Извиняюсь, ну вскипело просто. Можете не приводить свои аргументы, я вам просто скажу — где-то вы ошибаетесь. Так мало хорошей документации на русском и тут еще такие комментаторы.
          Автор, переводите еще. Плюсы вам везде куда можно.
            0
            Спасибо большое :)
              0
              Проблема не в том, что человек делает какое-то дело и переводит все это. Просто он переведет всю документацию (в лучше случае), но на ее обновления инициативы уже не останется. И будет лежать очередные неактуальные груды текста, путая новичков.
              • UFO just landed and posted this here
                  0
                  Если вас что-то не устраивает в этом переводе, большая просьба написать мне об этом. Я постараюсь исправить.
              0
              Мне вот что интересно — а чем вызвана необходимость объявлять внешние ключи только в одной модели? Что будет, если объявить ManyToMany в обеих?

              Просто как-то непривычно это видеть после ORM'а Kohan'ы…
              • UFO just landed and posted this here
                  0
                  Меня интересует алгоритм, сравнить с имеющимися в используемом мной php-фреймворке. Django я не никогда не использовал.
                  • UFO just landed and posted this here
                      0
                      Гм… странное решение. Ведь ManyToMany в принципе равноправная связь, обе модели могут быть заинтересованы в ее использовании напрямую.
                      • UFO just landed and posted this here
                          0
                          Я сейчас изучаю модуль Sprig для Kohana, он сделан по принципам моделей Django, потому и интересуюсь. Очень непривычная схема получается :)
                          • UFO just landed and posted this here
                  0
                  А скиньте кусок кода, плз :)
                    0
                    Какого кода? Любого? Php пойдет?
                    :)
                      0
                      Конечно на php :)
                      «Что будет, если объявить ManyToMany в обеих?». Вот эти пару моделек, если не сложно.
                        0
                        вот тут лежат две стандартные модели для модуля Auth Kohana v3.0. Между ними связь ManyToMany (она объявляется как HasMany + спец. параметр through для указания промежуточной таблицы).

                        В версии 2.3.4 ORM использовал отдельное свойство $has_and_belongs_to_many.
                          0
                          Спасибо. В PHP приходится явно указывать то, что в Python'е можно сделать динамически. Отсюда и разница в подходах фреймворков.
                            0
                            Что именно? Если мне не изменяет память, параметр 'model' может быть опущен, в таком случае он сгенерируется из названия связи (только в единственном числе). Есть также возможность указать имена внешних ключей, если они отличаются от стандартных (типа «user_id»).
                            • UFO just landed and posted this here
                                0
                                В Python есть setattr ;) Этим Django нагло пользуется.
                                • UFO just landed and posted this here
                                    +1
                                    Да, я вот начал изучение Питона и Джанги после РНР и фреймворка Symfony с Propel в качестве ORM. Странный подход, насчет того что указывать только в одной модели отношение м2м, как-то не читабельно получается, и опять-таки, получается что отношение неравноправное. Но наверно к этому надо привыкать просто.

                                    Надеюсь у меня получится, т.к. местами прочитать код на питоне очень тяжело, по сравнению с кодом в РНР. Но это, скорее всего, привычка.
                                      0
                                      Разве не логично, что раз отношение m2m, то связь двусторонняя, ведь в отношение вовлечены обе стороны? :) Просто становится меньше писанины.

                                      Как написали ниже:
                                      PS. Поначалу это кажется магией :) Но это просто питон.

                                      замечательная фраза :D
                                      • UFO just landed and posted this here
                                          0
                                          Насколько я понял, ему не нравится, что несмотря на то что отношение равноправное, отношение объявляется лишь в одной модели ) дискриминация
                    +2
                    Спасибо за топик.Так мало русскоязычной документации.
                    Вам бы сайтик, и все туда складывать. Почему практически у всех фреймворков есть русскоязычные сайты поддержки, а у django нет.
                    Я думаю при наличии нескольких заинтересованных людей можно организовать подобный ресурс.
                    • UFO just landed and posted this here
                        +1
                        Это не сайт, а ветка форума.
                        Хотя я с вами согласен это на сегодняшний день максимум о django в рунете.
                      0
                      Кстати, автор, вам надо указать, под какой лицензией распространяется ваш перевод. Если она будет какой надо, то в дальнейшем люди могли бы помочь с обновлением.
                        0
                        Народ, ну вы тут и развели, не нравиться статья — не читайте! Понаписали что вам и как не нравиться, дайте людям обсудить особенности документации, что не понятно. Нет, нужно каждому сказать, зачем да почему, да мне это не нравится. И еще раз, не нравиться -> Не читай!
                          0
                          И все-таки, как быть со свзью многие-ко-многим, если хочеться в обоих классах иметь коллекции
                          class User1(models.Model):
                          pk_user1_id = models.AutoField(primary_key=True, db_column='pk_user1_id')
                          name = models.CharField(max_length=135, blank=True)
                          organizations = models.ManyToManyField(Organization1, db_table=u'User1_has_Organization1', related_name='organizations')
                          class Meta:
                          db_table = u'User1'

                          class Organization1(models.Model):
                          pk_organization1_id = models.AutoField(primary_key=True, db_column='pk_Organization1_id') # Field name made lowercase.
                          name = models.CharField(max_length=135, blank=True)
                          users = models.ManyToManyField(User1, db_table = u'User1_has_Organization1', related_name='users')
                          class Meta:
                          db_table = u'Organization1'

                          И это не работает:(
                          И как указать на какой столбец в промежуточной таблице должна мапиться связь? Потому, что джанго хочет конкретный столбец(organization1_id), а у меня в базе в этой таблице столбцы с иными названиями(fk_organization_id)?
                            +1
                            Из объекта Organization1 всегда же можно получить список User1, которые в него входят. Или я чего-то не понимаю в вашем вопросе?
                              0
                              Проблема в том, что я хочу у юзера иметь коллекцию организаций, а у организации коллекцию юзеров, что не понятно?
                                0
                                А какая разница? Я не совсем понимаю бизнес-логику данного желания.
                                  0
                                  В смысле какая разница, связь многие-ко-многим, и у того, и утого хочется иметь коллекции, а это не работает!
                                    +1
                                    Давайте проясним.

                                    У объекта my_user есть коллекция [org1, org2, org3]
                                    У объекта my_user2 есть коллекция [org1, org3]

                                    В таком случае:
                                    У объекта org1 есть коллекция [my_user, my_user2]
                                    У объекта org2 есть коллекция [my_user]
                                    У объекта org3 есть коллекция [my_user, my_user2]

                                    Всё так?
                                      0
                                      Да, вопрос в том, ели в модели не указывать в одном из классов коллекцию, как ее получить?(Я до это в java работал c JPA поэтому возникают такие вопросы)
                                        +1
                                        В вашем случае у объекта org1 есть коллекция organizations (то есть так, как вы прописали related_name) в модели User1.
                                        org1.organizations.all() — список всех User1, входящих в эту организацию.

                                        PS. Поначалу это кажется магией :) Но это просто питон.
                                        • UFO just landed and posted this here
                                            0
                                            Благодарю, разобрался. Долго не мог понять почему не работает *_set, оказыватся надо для обратной связи надо вызывать related_name(если он задан), *_set(если не задан).
                                            • UFO just landed and posted this here
                              • UFO just landed and posted this here
                                0
                                Проблема в том, что я хочу у юзера иметь коллекцию организаций, а у организации коллекцию юзеров, что не понятно?
                                  0
                                  Очень актуально для меня. Если перевод будет продолжаться (что очень хочется), то буду изучать Django по нему.
                                    0
                                    Будут желающие, перевод будет продолжаться :)
                                      0
                                      А поддерживаться актуальность уже переведённых разделов будет?
                                        0
                                        Одному делать это сложно, но я буду стараться )
                                    0
                                    Одному делать это сложно, но я буду стараться )
                                      0
                                      Извините, я с телефона. Видимо, куда-то не туда нажал :(
                                      0
                                      вообще, это хорошо, когда делают переводы… это делает программу более популярной, что в целом всем выгоднее…

                                      но когда переводят документацию к такой системе как Django — это кажется не очень правильным, она ведь завтра изменится, а вы не сможете все отследить, что и где изменилось, так как не сразу понятно, где и какие были правки… много их… да и статей там over 9000…

                                      в любом случае удачи, думаю, ваш труд не пропадет напрасно — некоторые прочитают, составят себе хотя бы общее представление о системе…

                                      но разобраться в ней не зная английского лично мне кажется нереальным…
                                        0
                                        Спасибо. На данный момент не ставится задача перевести все статьи, мне хочется просто задать некую отправную точку для начинающих и, как вы и сказали, дать общее представление о системе. Насчет обновления это да. Возможно, удастся решить и эту проблему :)
                                          0
                                          (Скопировал из первой части)

                                          Вы молодец! Вы проделали очень хорошую, очень большую работу.

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

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

                                          Если вы всё же хотите поучаствовать и быть полезным, помогите российским разработчикам переводить их документацию на английский, помогите переводить пользовательскую документацию и интерфейс Gnome на русский, там у них сейчас ой как руки нужны…
                                            0
                                            Спасибо, я помню ваше сообщение :)

                                          Only users with full accounts can post comments. Log in, please.