Как передать родительский идентификатор как fk в ModelForm дочернего объекта, используя общие представления на основе классов в Django?
Я пытаюсь использовать Django Generic Class-Based Views для построения интерфейса CRUD для двухмодельной базы данных. У меня есть рабочий интерфейс CRUD для родительской модели, и я застрял, пытаясь заставить ребенка работать. Для согласованности с другими примерами Django, выберите родителя как автора, а потомка - как книгу. Какой самый простой способ разрешить пользователям добавлять книги автору?
С точки зрения HTML, я думаю, что я хочу сделать ссылку на странице сведений об авторе, которая включает в себя идентификатор автора, предварительно установить этот идентификатор в форме книги, а затем обработать форму книги, используя этот идентификатор в качестве ПК Книги. Но я не понимаю, как использовать Django, чтобы это произошло. Я прочитал https://docs.djangoproject.com/en/1.6/topics/class-based-views/generic-editing/, Как использовать CreateView с ModelForm, Как установить исходные данные в классе Django основанный универсальный createview с данными запроса, и Установите начальное значение для modelform в основанных на классе универсальных представлениях, каждое из которых, кажется, отвечает на немного другой вопрос.
Вот соответствующий код:
models.py
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author)
title = models.CharField(max_length=500)
views.py
class BookCreate(CreateView):
form_class = BookForm
def get_success_url(self):
return reverse('myapp:author_read',args=(self.object.author.pk))
forms.py
class BookForm(forms.Modelform):
class Meta:
model = Book
urls.py
url(r'^(?P<pk>\d+)/$', AuthorRead.as_view(), name='author_read'),
url(r'^book/create/(?P<author_id>\d+)/$', BookCreate.as_view(), name='book_create'),
шаблоны / MyApp/author_detail.html
...
<p><a href="{% url 'myapp:book_create' author_id=Author.pk %}">Add a book</a></p>
...
шаблоны / MyApp/book_form.html
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Done">
</form>
Вопросы
1) Как получить идентификатор автора из URL-адреса страницы книги в форме автора, а затем обработать правильно? В приведенном выше примере кода отладчик Django показывает, что он представлен следующим образом:
View function Arguments Keyword arguments URL name
myapp.views.BookCreate () {'author_id': u'1234'} book_create
но я не понимаю, как извлечь эту переменную из... контекста? ... и положить его в форму.
1a) Могу ли я сделать его параметром url вместо части самого URL, т.е. book/create?author=1234
вместо book/create/1234/
? Или даже сделать все это POST, чтобы он не был частью URL? Какая лучшая практика и как это делается?
2) Как только переменная находится в форме, как она может быть представлена в качестве скрытого ввода, чтобы пользователю не приходилось ее видеть?
3 ответа
С URL, который вы определили в author_detail.html
переменная author_id будет доступна в виде self.kwargs['author_id']
# views.py
class BookCreate(CreateView):
...
def form_valid(self, form):
book = form.save(commit=False)
author_id = form.data['author_id']
author = get_object_or_404(Author, id=author_id)
book.author = author
return super(BookCreate, self).form_valid(form)
...
def get_context_data(self, **kwargs):
context = super(BookCreate, self).get_context_data(**kwargs)
context['a_id'] = self.kwargs['author_id']
return context
Тогда в вашей форме вы можете добавить:
class BookForm(forms.Modelform):
...
def __init__(self, *args, **kwargs):
self.fields["author_id"] = forms.CharField(widget=forms.HiddenInput())
super(BookForm, self).__init__(self, *args, **kwargs)
Тогда в шаблоне:
<input type=hidden name="author_id" value="{{ a_id }}">
Form_valid в представлении должен извлечь идентификатор, получить соответствующего автора и установить этого автора в качестве автора книг. commit=False
предотвращает сохранение модели вначале, пока вы устанавливаете автора, и вызов super приведет к form.save(commit=True)
будучи призванным
У меня была похожая ситуация, и при выполнении принятых шагов ответа я столкнулся с 2 ошибками (я использую Python 2.7):
- У объекта нет атрибута 'fields', который был исправлен с помощью ответа на аналогичный вопрос: /questions/29961867/dinamicheski-obnovlyat-nabor-zaprosov-modelmultiplechoicefield-v-modelform/29961875#29961875 от @scotchandsoda:
...self.fields должен быть помещен перед вызовом super(...)
def __init__(self, users_list, **kw):
super(BaseWriteForm, self).__init__(**kw)
self.fields['recipients'].queryset = User.objects.filter(pk__in=users_list)
- У объекта нет атрибута 'get', который был исправлен с помощью ответа: /questions/722858/obekt-ne-imeet-atributa-get/722871#722871 from @luke_dupin:
... эта ошибка также может быть сгенерирована неправильной передачей аргументов в инициализации формы, которая используется для модели администратора.
Пример:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(self, *args, **kwargs)
Заметили двойное прохождение себя? Так должно быть:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
Вы можете передать идентификатор автора в форму, вот несколько указаний:
class BookForm(forms.Modelform):
author = None
class Meta:
model = Book
def __init__(self, *args, **kwargs):
self.author = kwargs.pop('author')
super(BookForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
# your custom save (returns book)
class BookCreate(CreateView):
form_class = BookForm
def get_form_kwargs(self):
kwargs = super(BookCreate, self).get_form_kwargs()
kwargs['author'] = # pass your author object
return kwargs