LINUX.ORG.RU

[django]формы и модели

 


0

2

Второй вечер втыкаю, никак не пойму.

есть модель (лишние поскипано)

class Transaction(models.Model):
        date = models.DateField()
        person = models.ForeignKey(Person)
        amount = models.DecimalField(max_digits=10, decimal_places=2)

в некоем view, в котором есть объект Person нужно вывести форму для добавления Transaction. Если я делаю форму с помощью ModelForm то поле person представляется как список выбора со всеми доступными объектами из Person, а мне нужно создать Transaction именно для текущего Person, без возможности выбора других, проще говоря person в форме должен быть в hidden поле и иметь определенное значение. Можно ли такое реализовать с помощью ModelForm, или надо делать форму ручками?


Передаешь person в конструктор формы, там инициализируешь поле и устанавливаешь скрытый виджет.

На память, что-то такое:

def __init__(self, *args, **kwargs):
  person = kwargs.pop('person')
  super(YourForm, self).__init__(*args, **kwargs)

  self.fields['person'].initial = person
  self.fileds['person'].widget = HiddenInput()

Подробнее смотри в доках.

anonymous
()
Ответ на: комментарий от anonymous

Почти верно, но надо еще ограничить queryset, во избежание.

self.fileds['person'].queryset = Person.objects.filter(pk=person)

ну и лучше определить виджет для формы более явно

class TransForm(forms.ModelForm):
    person = forms.ModelChoiceField(queryset=Person.objects.all(), widget=forms.HiddenInput)

Apkawa
()

лучше уж вообще тогда такого поля в форме не делать (или исключать или просто в fields не вносить), а далее

def view(request):
  class Transaction(SomeCoolBroolForm):
    def save(self, commit = True):
      transaction = super(Transaction, self).save(commit = False)
      transaction.person = ...
      transaction.save()
      return transaction

trashymichael ★★★
()
Ответ на: комментарий от trashymichael

благодарствую, буду пробовать.

CFA
() автор топика
Ответ на: комментарий от anonymous

в конструктор надо передавать сам person или же person.pk? на это

form = models.TransactionForm(person=person)

получаю «int() argument must be a string or a number, not 'Person'» - ругается на установку queryset - self.fields['person'].queryset = Person.objects.filter(pk=person) Если же передаю person.pk, то все ок.

CFA
() автор топика
Ответ на: комментарий от CFA

Во-первых, как сказали Apkawa, widget и queryset лучше определить явно. Но запрашивать варианты Person не надо, поэтому: queryset = Person.objects.none(). Вообще-то queryset запрос ленивый и с HiddenInput и так не выполнится, но пусть будет.

class TransactionForm(forms.ModelForm):
  person = forms.ModelChoiceField(queryset=Person.objects.none(), widget=forms.HiddenInput)

  class Meta:
    model = Person

В конструкторе только остается инициализация (с проверочкой):

def __init__(self, *args, **kwargs):
  person = kwargs.pop('person', None)
  super(YourForm, self).__init__(*args, **kwargs)
  
  if person:
    self.fields['person'].initial = person


Во-вторых, а надо ли тебе это скрытое поле вообще? Ты же получаешь id person через url?

def add_transaction(self, person_id):
  person = get_object_or_404(Person, pk=person_id)

Так? Тогда, как сказал trashymichael, исключай вообще это поле из формы:

class TransactionForm(forms.ModelForm):
  exclude = (person,)
  class Meta:
    model = Person

И view:

  form = models.TransactionForm(request.POST)
  transaction = form.save(commit=false)
  transaction.person = person
  transaction.save()


Скорее всего у тебя второй вариант. Лучше описывать свою проблему хорошо заранее.

anonymous
()
Ответ на: комментарий от anonymous

пожалуй получать id person из url и правда изящнее, но все же) self.fileds['person'].queryset = Person.objects.filter(pk=person) в конструкторе нужен или достаточно Person.objects.none() в конструкторе поля? Если я правильно понял, этот queryset используется для проверки id на валидность?

CFA
() автор топика
Ответ на: комментарий от CFA

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

trashymichael ★★★
()
Ответ на: комментарий от CFA

http://docs.djangoproject.com/en/1.2/ref/forms/fields/#django.forms.ModelChoi...

A QuerySet of model objects from which the choices for the field will be derived, and which will be used to validate the user's selection.

Да, судя по докам, используется еще и для проверки. Так что в конструкторе свой вариант оставь, только не filter(pk=person), а filter(pk=person.pk).

Кстати, учти еще, что клиент может подменить id в скрытом поле.

anonymous
()
Ответ на: комментарий от anonymous

вот объясните мне, зачем там вообще это поле городить?
конечно, если будет ModelChoiceField то и валидация будет по queryset, но зачем все это в даном случае? что бы валидировать по выборке из одной модели?

trashymichael ★★★
()
Ответ на: комментарий от trashymichael

> вот объясните мне, зачем там вообще это поле городить?

Ты это у ТС'а спрашивай. Я тоже считаю, что это избыточно и не нужно.

С другой стороны, ТС предоставил очень скудные данные и мы можем только догадываться о том, что ему действительно нужно. Может у него архитектура изначально кривая.

anonymous
()
Ответ на: комментарий от anonymous

Я вроде согласился, что выдирать id person из url удобнее, и вопрос про queryset имел скорее академический интерес. Задача у меня крайне простая, и в общем то изложена полностью - есть несколько Person, и у каждой есть набор Transaction с какими то атрибутами. Надо иметь возможность добавлять/удалять эти самые Person и Transactin для каждой из них.

CFA
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.