Формы Django: внешний ключ в скрытом поле - PullRequest
12 голосов
/ 07 марта 2009

Моя форма:

class PlanForm(forms.ModelForm):    
    owner = forms.ModelChoiceField(label="",
                                  queryset=Profile.objects.all(),
                                  widget=forms.HiddenInput())
    etc...

    class Meta:
        model = Plan

Владелец в модели - это ForeignKey для профиля.

Когда я устанавливаю эту форму, я устанавливаю значение «owner» в качестве объекта Profile.

Но когда это появляется в форме, кажется, что оно содержит имя профиля, например:

<input type="hidden" name="owner" value="phil" id="id_owner" />

Когда форма отправляется и возвращается к моим views.py, я пытаюсь обработать ее следующим образом:

    form = PlanForm(request.POST)
    ...
    if form.is_valid():                
        plan = form.save()
        return HttpResponseRedirect('/plans/%s'%plan.id) # Redirect after POST

Тем не менее, я получаю ошибку преобразования типа, поскольку не удается преобразовать строку «phil» (имя пользователя, которое было сохранено в поле «owner») в Int, чтобы превратить ее в ForeignKey.

Так что здесь происходит? Должна ли ModelForm представлять внешний ключ как число и прозрачно обрабатывать его? Или мне нужно извлечь идентификатор самостоятельно в поле владельца формы? И если да, то как и когда я могу отобразить его обратно, прежде чем пытаться проверить форму?

Ответы [ 5 ]

18 голосов
/ 07 марта 2009

Я подозреваю, что метод __unicode__ для экземпляра модели профиля или его repr настроен на возврат значения, отличного от self.id. Например, я просто настроил это:

# models.py
class Profile(models.Model):
    name = models.CharField('profile name', max_length=10)

    def __unicode__(self):
        return u'%d' % self.id

class Plan(models.Model):
    name = models.CharField('plan name', max_length=10)
    profile = models.ForeignKey(Profile, related_name='profiles')

    def __unicode__(self):
        return self.name


# forms.py
class PlanForm(forms.ModelForm):
    profile = forms.ModelChoiceField(queryset=Profile.objects.all(),
            widget=forms.HiddenInput())

    class Meta:
        model = Plan

# views.py
def add_plan(request):

    if request.method == 'POST':
        return HttpResponse(request.POST['profile'])


    profile = Profile.objects.all()[0]
    form = PlanForm(initial={'profile':profile})
    return render_to_response('add_plan.html',
            {
                'form':form,
            },
            context_instance=RequestContext(request))

При этом я вижу PlanForm.profile, отображенный таким образом в шаблоне:

<input type="hidden" name="profile" value="1" id="id_profile" />
11 голосов
/ 07 марта 2009

Хмм ...

Это может быть дыра в безопасности.

Предположим, злонамеренный злоумышленник создал POST (скажем, с помощью XmlHttpRequest из FireBug) и установил в качестве значения для профиля какое-то дурацкое значение, например идентификатор вашего профиля. Наверное, не то, что вы хотели?

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

form = PlanForm(request.POST)
if form.is_valid():
    plan = form.save(commit=False)
    plan.owner = request.user.get_profile()
    plan.save()
    form.save_m2m() # if neccesary
8 голосов
/ 07 марта 2009

Когда вы назначаете объект Profile для формы, Django приводит его в соответствие и использует выходные данные в качестве значения в форме. Однако вы можете ожидать, что Django будет использовать идентификатор объекта.

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

form = PlanForm(initial={'profile': profile.pk})

С другой стороны, когда вы работаете со связанными формами, они работают гораздо разумнее:

form = PlanForm(request.POST)
if form.is_valid():
    print form.cleaned_data['profile']  # the appropriate Profile object
2 голосов
/ 20 ноября 2012

Поскольку ModelChoiceField наследуется от ChoiceFIeld, для этого следует использовать виджет MultipleHiddenInput :

class PlanForm(forms.ModelForm):    
  owner = forms.ModelChoiceField(
            queryset=Profile.objects.all(),
            widget=forms.MultipleHiddenInput())

  class Meta:
    model = Plan
1 голос
/ 07 марта 2009

Обычно нет необходимости помещать связанный объект в поле формы. Есть лучший способ, и это указание родительского идентификатора в URL-адресе формы.

Предположим, вам нужно визуализировать форму для нового объекта Plan, а затем создать ее, когда форма формируется. Вот как будет выглядеть ваш urlconf:

(r"/profile/(?P<profile_id>\d+)/plan/new", view.new_plan), # uses profile_id to define proper form action
(r"/profile/(?P<profile_id>\d+)/plan/create", view.create_plan) # uses profile_id as a Plan field

А если вы меняете существующий объект, все, что вам нужно, это plan_id, вы можете вывести из него любую связанную запись.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...