Django: сохранение внешнего ключа из формы

Это продолжение этого вопроса, в котором я пытаюсь понять как создать PointField, состоящий из lat/lon FloatFields. Я последовал совету @Simon и реструктурировал свою модель, чтобы она выглядела следующим образом:

class Point(models.Model):
   lat = models.FloatField()
   lon = models.FloatField()

class Thing(models.Model):
   point = models.ForeignKey(Point)

В моей форме есть два поля, соответствующие значениям координат долготы и широты карт Google:

class StepThreeForm(forms.Form):
    lat = forms.FloatField()
    lon = forms.FloatField()
    ...

Однако это не работает по понятным причинам, но я не уверен, как это исправить. Чтобы уточнить, я пытаюсь иметь два поля формы, соответствующие значениям внешнего ключа lat и lon. Вот дополнительная информация (я использую FormWizard и forms.Form):


url(r'^mapform/$', login_required(MyWizard.as_view([StepOneForm, StepTwoForm, StepThreeForm])), name='create'),

class MyWizard(SessionWizardView):  ## this view also serves to edit existing objects and provide their instances
    def done(self, form_list, **kwargs):
        id = form_list[0].cleaned_data['id']
        try:
            thing = Thing.objects.get(pk=id)
            instance = thing
        except:
            thing = None
            instance = None
        if thing and thing.user != self.request.user:
            raise HttpResponseForbidden()
        if not thing:
            instance = Thing()
            for form in form_list:
                for field, value in form.cleaned_data.iteritems():
                    setattr(instance, field, value)
            instance.user = self.request.user
            instance.save()
        return render_to_response('wizard-done.html', {
                'form_data': [form.cleaned_data for form in form_list],})

Я ценю любые советы и помощь!


РЕДАКТИРОВАТЬ: обновление на основе ввода Юдзи Томита. Большинство из них имели большой смысл (спасибо!), но я не уверен, почему это приводит к ошибке ValueError.

class MyWizard(SessionWizardView):
     ....
            for form in form_list:
                form.save(instance)
     ...

class StepOneForm(forms.Form):
     ...
     def save(self, thing):
        for field, value in self.cleaned_data.items():
            setattr(thing, field, value)

class StepTwoForm(forms.Form):
     ...
     def save(self, thing):
        for field, value in self.cleaned_data.items():
            setattr(thing, field, value)

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

class StepThreeForm(forms.Form):
    lat = forms.FloatField()
    lon = forms.FloatField()

    def save(self, thing):
        thing.point = Point.objects.get_or_create(lat=self.cleaned_data.get('lat'), lon=self.cleaned_data.get('lon'))

Это дает ValueError: Cannot assign "(<Point: Point object>, False)": "Thing.point" must be a "Point" instance.

Traceback:
File "/lib/python2.7/django/core/handlers/base.py" in get_response
  111.                         response = callback(request, *callback_args, **callback_kwargs)
File "/lib/python2.7/django/contrib/auth/decorators.py" in _wrapped_view
  20.                 return view_func(request, *args, **kwargs)
File "/lib/python2.7/django/views/generic/base.py" in view
  48.             return self.dispatch(request, *args, **kwargs)
File "/lib/python2.7/django/contrib/formtools/wizard/views.py" in dispatch
  223.         response = super(WizardView, self).dispatch(request, *args, **kwargs)
File "/lib/python2.7/django/views/generic/base.py" in dispatch
  69.         return handler(request, *args, **kwargs)
File "/lib/python2.7/django/contrib/formtools/wizard/views.py" in post
  286.                 return self.render_done(form, **kwargs)
File "/lib/python2.7/django/contrib/formtools/wizard/views.py" in render_done
  328.         done_response = self.done(final_form_list, **kwargs)
File "/myproject/myapp/forms.py" in done
  93.       form.save(instance)
File "/myproject/myapp/forms.py" in save
  67.         thing.point = Thing.objects.get_or_create(lat=self.cleaned_data.get('lat'), lon=self.cleaned_data.get('lon'))
File "/lib/python2.7/django/db/models/fields/related.py" in __set__
  366.                                  self.field.name, self.field.rel.to._meta.object_name))

person Nick B    schedule 04.10.2013    source источник
comment
Пожалуйста, отредактируйте свой исходный вопрос, а не распределяйте его по нескольким потокам.   -  person Ludwik Trammer    schedule 04.10.2013
comment
Насколько я понял, если в вопросе задавались другие вопросы, чем в оригинале, я должен был начать новый вопрос. Первый вопрос касался того, как filter использовать два отдельных поля модели. Был дан ответ. Эта тема отличается тем, что в ней спрашивается, как сохранить ForeignKey полей из формы, а это совершенно другое упражнение, чем фильтрация на основе комбинации двух полей модели. Если я ошибаюсь, буду признателен модератору за разъяснение. Спасибо!   -  person Nick B    schedule 05.10.2013
comment
@NickB, с тобой все в порядке. Мы все делаем внезапные предположения, когда читаем посты. Спасибо за гражданское отношение к этому!   -  person Yuji 'Tomita' Tomita    schedule 05.10.2013
comment
Да ты прав. Я неправильно понял цель вашего второго треда. Извини за это.   -  person Ludwik Trammer    schedule 05.10.2013
comment
Конечно, нет проблем, я ценю обратную связь. Я искренне хотел знать, нарушаю ли я условия форума, и я ценю разъяснения! Спасибо за помощь!   -  person Nick B    schedule 05.10.2013


Ответы (1)


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

Суть в том, что прямо сейчас у вас есть одеяло: «для каждого поля во всех формах установите атрибут Thing для этих полей».

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

class Form1(...):
   def save(self, thing):
      for field, value in self.cleaned_data.items():
          setattr(thing, field, value)

class Form2(...):
   def save(self, thing):
      thing.point = Point.objects.get_or_create(lat=self.cleaned_data.get('lat'), long=...)
      # note, you may not want get_or_create if you don't want to share points.

Тогда ваше представление станет:

for form in form_list:
    form.save(instance)

Просто идея.

Если вы хотите быть более СУХИМ об этом и любите автоматизировать другие ваши формы, я бы построил базовую форму, в которой уже определен метод сохранения:

class BaseSaveBehaviorForm(forms.Form):
     def save(self, thing):
         for field, value in self.cleaned_data.items():
             setattr(thing, field, value)

class NormalBehaviorForm(BaseSaveBehaviorForm):
     # your forms as usual


class SpecialSaveBehaviorForm(forms.Form):
     def save(self, instance):
         # do something unusual
person Yuji 'Tomita' Tomita    schedule 04.10.2013
comment
Вау, большое спасибо, это очень полезно и помогает объяснить, что происходит в формах. Спасибо! Однако я думаю, что реализовал это неправильно, поскольку моя попытка привела к ошибке ValueError. Если у вас есть минутка, было бы здорово узнать, что я, возможно, сделал неправильно, но если вы слишком заняты, я тоже пойму. Спасибо еще раз! - person Nick B; 05.10.2013
comment
@НикБ, без проблем. Ошибка по моей вине, get_or_create возвращает экземпляр и флаг создан или нет. Вы должны назначить thing.point = Point.objects.create(lat=lat, long=long) или thing.point = ...get_or_create()[0] - person Yuji 'Tomita' Tomita; 05.10.2013
comment
Спасибо за помощь пациенту. Я многому научился из вашего объяснения и теперь могу правильно сохранить объект в FormWizard! Спасибо еще раз! - person Nick B; 07.10.2013