Каковы шаги, чтобы заставить ModelForm работать с отношением ManyToMany с промежуточной моделью в Django? - PullRequest
30 голосов
/ 23 декабря 2008
  • У меня есть Клиент и Группа Модель.
  • A Клиент может быть частью нескольких групп .
  • Клиенты , являющиеся частью группы, могут использовать бесплатную арендную ставку своей группы в любое время, но только один раз. Вот тут-то и появляются промежуточные модели ( ClientGroupe ) с этими дополнительными данными.

На данный момент, когда я пытаюсь сохранить данные m2m, он просто умирает и говорит, что я должен использовать ClientGroupe Manager ... так чего не хватает?

Вот мои модели:

class Groupe(models.Model):
    nom = models.CharField(max_length=1500, blank=True)

class Client(models.Model):
    nom = models.CharField(max_length=450, blank=True)
    prenom = models.CharField(max_length=450, blank=True)
    groupes = models.ManyToManyField(Groupe, null = True, blank = True, through='ClientGroupe')

class ClientGroupe(models.Model):
    client = models.ForeignKey(Client)
    groupe = models.ForeignKey(Groupe)
    dt = models.DateField(null=True, blank=True) # the date the client is using its group's free rental rate    

    class Meta:
        db_table = u'clients_groupes'

и вот мой взгляд:

def modifier(request, id):
    client = Client.objects.get(id=id)    
    form = ClientForm(instance = client)

    dict = {
        "form": form
        , "instance" : client
    }

    if request.method == "POST":
        form = ClientForm(request.POST, instance = client)

        if form.is_valid():
            client_mod = form.save()

            id = client_mod.id
            return HttpResponseRedirect(
                "/client/%(id)s/?err=success" % {"id" : id}
            )
        else:
            return HttpResponseRedirect(
                "/client/%(id)s/?err=warning" % {"id" : id}
            )

    return render_to_response(
        "client/modifier.html"
        , dict
        , context_instance=RequestContext(request)
    )

EDIT :

и вот код ClientForm:

class ClientForm(ModelForm):
    class Meta:
        model = Client

РЕДАКТИРОВАТЬ # 2 : Вот сообщение об ошибке:

AttributeError at /client/445/

Cannot set values on a ManyToManyField which specifies an intermediary model. Use ClientGroupe's Manager instead.

Request Method:     POST
Request URL:    http://localhost/client/445/
Exception Type:     AttributeError
Exception Value:    Cannot set values on a ManyToManyField which specifies an intermediary model.  Use ClientGroupe's Manager instead.

Exception Location:     C:\Python25\lib\site-packages\django\db\models\fields\related.py  in __set__, line 574
Python Executable:  C:\xampp\apache\bin\apache.exe
Python Version:     2.5.2

Ответы [ 5 ]

18 голосов
/ 31 мая 2009

Если вы используете метод сохранения прямо сейчас, Django попытается сохранить с помощью менеджера (что Django не позволяет). К сожалению, поведение, которое вам нужно, немного сложнее, чем то, что ModelForm делает по умолчанию. Что вам нужно сделать, это создать formset .

Прежде всего, вам нужно изменить параметры вашего ClientForm, чтобы он не отображал атрибут groupes.

class ClientForm(ModelForm):
    class Meta:
        model = Client
        exclude = ('groupes',)

Далее необходимо изменить вид для отображения набора форм:

from django.forms.models import inlineformset_factory

def modifier(request, id):
    client = Client.objects.get(id=id)    
    form = ClientForm(instance = client)
    # Create the formset class
    GroupeFormset = inlineformset_factory(Client, Groupe)
    # Create the formset
    formset = GroupeFormset(instance = client)

    dict = {
        "form": form
        , "formset" : formset
        , "instance" : client
    }

    if request.method == "POST":
        form = ClientForm(request.POST, instance = client)
        formset = GroupeFormset(request.POST, instance = client)

        if form.is_valid() and formset.is_valid():
            client_mod = form.save()
            formset.save()

            id = client_mod.id
            return HttpResponseRedirect(
                "/client/%(id)s/?err=success" % {"id" : id}
            )
        else:
            return HttpResponseRedirect(
                "/client/%(id)s/?err=warning" % {"id" : id}
            )

    return render_to_response(
        "client/modifier.html"
        , dict
        , context_instance=RequestContext(request)
    )

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

Если вам нужны какие-либо другие советы по наборам форм, см. Следующие статьи:

Модель Formsets
FormSets

10 голосов
/ 07 октября 2010
…
if form.is_valid():
    client_mod = form.save(commit=False)
    client_mod.save()
    for groupe in form.cleaned_data.get('groupes'):
        clientgroupe = ClientGroupe(client=client_mod, groupe=groupe)
        clientgroupe.save()
    …
4 голосов
/ 18 февраля 2009

Возможно, вам нужно удалить поле ManyToMany из вашей модели клиента, или же тщательно исключить его из вашей формы. К сожалению, виджет по умолчанию для поля ManyToMany не может заполнить модель ClientGroupe должным образом (даже если для отсутствующего поля dt задано значение autonow = True). Это то, что вам нужно либо разбить на другую форму, либо обработать в своем представлении.

0 голосов
/ 15 января 2014

Я предоставляю альтернативное решение из-за проблем, с которыми я столкнулся при вызове forms_valid:

class SplingCreate(forms.ModelForm):
class Meta:
    model = SplingModel
    fields = ('Link', 'Genres', 'Image', 'ImageURL',)

def save(self, commit=True):
    from django.forms.models import save_instance

    if self.instance.pk is None:
        fail_message = 'created'
    else:
        fail_message = 'changed'
    fields = set(self._meta.fields) - set(('Genres',))
    instance = save_instance(self, self.instance, fields,
                             fail_message, commit, construct=False)

    genres = self.cleaned_data.get('Genres')
    for genre in genres:
        SplingGenreModel.objects.get_or_create(spling=instance, genre=genre)

    return instance

Я скопировал логику из djangos forms / models.py, мое поле Жанры - это множество данных с промежуточной таблицей - я исключаю его из save_instance, а затем сохраняю отдельно.

0 голосов
/ 26 января 2009

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

clientgroupe = ClientGroupe.objects.create(client=client_instance, groupe=groupe_instance, dt=datetime.datetime.now())

где client_instance и groupe_instance ваш клиент и группа объектов.

...