Когда и как создаются отношения «многие ко многим» при сохранении модели? - PullRequest
5 голосов
/ 07 октября 2011

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

Models.py:

class Document(models.Model):
    title = models.CharField(max_length=50, unique=True)
    title_slug = models.SlugField(max_length=50, unique=True, editable=False)
    labels = models.ManyToManyField('Label')

    def save(self, *args, **kwargs):
        self.title_slug = slugify(self.title)
        super(Document, self).save(*args, **kwargs)

class Label(models.Model):
    name = models.CharField(max_length=40, unique=True)
    slug = models.SlugField(max_length=40, unique=True, editable=False)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super(Document, self).save(*args, **kwargs)

Views.py:

class DocumentForm(ModelForm):
    class Meta:
        model = Document
        fields = ["title","labels"]

def upload_document(request):
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            new_document = form.save()
            return HttpResponseRedirect("/thanks/")

    else:
        form = DocumentForm()

    return render_to_response('upload_page.html', {'form':form}, context_instance=RequestContext(request))

Когда я загружаю документ, он добавляется в базу данныхОднако метки не создаются и не связаны с документом.Нужно ли что-то явно добавлять в функцию save () Document, чтобы это произошло?Или где-нибудь в файле Views.py?Я предполагаю, что это будет выглядеть примерно так:

  • Проверьте, существует ли добавляемый ярлык, уже существует
  • Если его нет, создайте новый ярлык * 1014.*
  • Получение как текущего document_id, так и нового / существующего label_id
  • Добавление записи в таблицу document_labels (автоматически создается для отношения «многие ко многим»)

IЯ чувствую, что это довольно стандартная функциональность, которая, как я предполагал, будет встроена в отношения «многие ко многим» в django, но пока мне это не помогает.Я пытаюсь не изобретать колесо здесь.Немного нового в django.

Заранее спасибо!

Ответы [ 5 ]

4 голосов
/ 01 ноября 2011

Как говорили другие люди, вы не можете сохранить в одноразовом объекте Document и его поле ManyToMany, потому что django создает " таблицу промежуточного соединения ", для которой требуется идентификатор объекта Document, который при этом не определен точка.

В ModelForm есть функция save_m2m, которая должна вызываться самой формой, как описано в документе

Однако, если это не сработает, возможно, уловка состоит в том, чтобы вызвать save_m2m в функции просмотра, например:

def upload_document(request):
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            new_document = form.save()
            form.save_m2m()
            return HttpResponseRedirect("/thanks/")

    else:
        form = DocumentForm()

    return render_to_response('upload_page.html', {'form':form}, context_instance=RequestContext(request))

Надеюсь, это поможет, Stéphane

2 голосов
/ 01 ноября 2011

Я думаю, что это легко решить, если вы понимаете формы и фабрику django

Может быть, этот документ поможет вам:

https://docs.djangoproject.com/en/1.3/topics/forms/modelforms/#inline-formsets

2 голосов
/ 27 октября 2011

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

Предполагается, что это то, что выхочу,

в views.py

def upload_document(request):
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            labels = request.POST.getlist('labels')
            new_document = form.save()
            for label_id in labels: # we're only going to add currently defined labels
                label = Label.objects.get(id=int(label_id))
                new_document.labels.add(label)
            new_document.save()
            return HttpResponseRedirect("/thanks/")
    else:
        form = DocumentForm()

    return render_to_response('doc_form.html', {'form':form}, context_instance=RequestContext(request))

Я обновил модель для Label в models.py ,

class Label(models.Model):
    name = models.CharField(max_length=40, unique=True)
    slug = models.SlugField(max_length=40, unique=True, editable=False)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super(Label, self).save(*args, **kwargs)

    def __unicode__(self):
        return self.name

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

for label in labels: # labels entered by user
    try:
        lbl = Label.objects.get(name='label')
    except Label.DoesNotExist:
        lbl = None

    if not lbl:
        lbl = Label()
        lbl.name = label
        lbl.save()

    newDoc.labels.add(lbl)

newDoc.save()

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

2 голосов
/ 07 октября 2011

Я бы посоветовал обратиться к тому, как приложение Django Admin работает в подобных ситуациях. Как правило, это будет двухэтапная операция; Сначала нужно создать несколько меток, затем создать документ, выбрать метки, которые вы хотите связать, из списка с множественным выбором, а затем сохранить его. Затем Django автоматически связывает метки, выбранные в списке, через таблицу «многие ко многим» между документами и метками.

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

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

0 голосов
/ 27 октября 2011

Связанные объекты не создаются автоматически при сохранении (). Вам следует создать еще одну форму для ярлыков и сохранить их в явном виде.

...