2012-01-25 2 views
5

У меня есть две модели:Сохранить новый внешний ключ с формой Джанго

class Studio(models.Model): 
    name = models.CharField("Studio", max_length=30, unique=True) 

class Film(models.Model): 
    studio = models.ForeignKey(Studio, verbose_name="Studio") 
    name = models.CharField("Film Name", max_length=30, unique=True) 

У меня есть форма пленки, которая позволяет пользователю выбрать либо существовавшие ранее Studio, или введите новый:

class FilmForm(forms.Form): 
    studio = forms.ModelChoiceField(Studio.objects, required=False) 
    new_studio = forms.CharField(max_length=30, required=False, label = "New Studio Name") 
    name = forms.CharField(max_length=30, label = "Film Name") 

Существует подтверждение, чтобы имя new_studio еще не было. Если пользователь вводит новый_студий, я хочу сохранить студию, а затем сохранить новый фильм.

form = FilmForm(request.POST) 
if form.is_valid(): # All validation rules pass 
    std = Studio(name = form.cleaned_data['new_studio']) 
    std.save() 

Но как же сохранить экземпляр фильма в соответствии с новым номером студии? Я видел this question, но что, если у меня есть еще много полей в модели фильма и в форме фильма? Если я использую связанный ответ, мне нужно будет указать каждое поле:

studio = Studio.objects.get(name=request.POST['new_studio']) 
newFilm=Film(name=form.name, studio=studio, field_one = form.field_one, field_two = form.field_two, etc.) 

Каков правильный способ его реализации?

ответ

5

Действительно, ваша единственная проблема в том, что вы использовали стандарт Form вместо ModelForm. Form не имеет метода save, потому что он по своей сути не привязан ни к чему (т. Е. Он не знает, что сохранить или где сохранить).

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

class FilmForm(forms.ModelForm): 
    class Meta: 
     model = Film 

    # only need to define `new_studio`, other fields come automatically from model 
    new_studio = forms.CharField(max_length=30, required=False, label = "New Studio Name") 

    def __init__(self, *args, **kwargs): 
     super(FilmForm, self).__init__(*args, **kwargs) 
     # make `studio` not required, we'll check for one of `studio` or `new_studio` in the `clean` method 
     self.fields['studio'].required = False 

    def clean(self): 
     studio = self.cleaned_data.get('studio') 
     new_studio = self.cleaned_data.get('new_studio') 
     if not studio and not new_studio: 
      # neither was specified so raise an error to user 
      raise forms.ValidationError('Must specify either Studio or New Studio!') 
     elif not studio: 
      # get/create `Studio` from `new_studio` and use it for `studio` field 
      studio, created = Studio.objects.get_or_create(name=new_studio) 
      self.cleaned_data['studio'] = studio 

     return super(FilmForm, self).clean() 

Затем, на ваш взгляд, все, что вам нужно:

if form.is_valid(): 
    form.save() 
+0

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

+0

У меня есть один вопрос.Я создал проверку проверки для дублированной студии (если пользователь вводит новый_студий, который уже существует). Код правильно достигает ValidationError, но, похоже, продолжает очищаться и дает мне ошибку: нельзя назначить None: «Film.studio» не разрешает нулевые значения. –

+0

«Чистый» метод позаботится обо всем этом. Если существует 'new_studio', он просто получит существующую студию вместо фактического создания нового (' get_or_create'), поэтому проверка проверки вам не нужна. Единственный случай, не описанный выше методом 'clean', заключается в том, что введена' studio', но 'new_studio' - нет. Однако это не будет ошибкой, и в этом сценарии не должно быть ничего особенного. Я предполагаю, что любая проверка проверки, которую вы делаете, как-то возится со значениями. –

2

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

Django поставляется с кодом Inline Formsets, который поможет вам управлять отношениями в форме.

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

from django.forms.models import inlineformset_factory 

Тогда:

YOUR_FORMSET = inlineformset_factory(Model1,Model2) 

Назад на Yout, views.py вы должны импортировать как ваши формы и новый созданный YOUR_FORMSET.

После этого, ваш метод должен использовать inlineformset так:

def add_question(request): 

    if request.method == 'POST': 


     form =AddQuestionForm(request.POST, request.FILES) 
     if form.is_valid(): 
      new_question = form.save(commit=False) 

      question_formset = QuestionFormset(request.POST, instance=new_question) 
      if question_formset.is_valid(): 
       form.save() 
       question_formset.save() 
       return HttpResponseRedirect(reverse('polls:detail',args=(new_question.pk,))) 
     else: 
      print(form.errors) 
    else: 
     form = AddQuestionForm() 
     question_formset = QuestionFormset(instance=Question()) 
    return render(request, 'polls/add.html', {'form':form, 'question_formset':question_formset,}) 

Я использую свой собственный пример, но идея объясняется.

Если вы хотите прочитать более конкретное объяснение, прочитайте это замечательное blog post, которое я нашел.

+0

Спасибо за отличную запись в блоге :) – Martin

+0

Надеюсь, это поможет большему количеству людей :) – villancikos

+0

ссылка дает 404 – AmiNadimi

Смежные вопросы