Как передать родительский идентификатор как 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):

  1. У объекта нет атрибута '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)
  1. У объекта нет атрибута '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
Другие вопросы по тегам