Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
264 views
in Technique[技术] by (71.8m points)

python - How do I pass a parent id as an fk to child object's ModelForm using generic class-based views in Django?

I am trying to use Django Generic Class-Based Views to build a CRUD interface to a two-model database. I have a working CRUD interface to the parent model, and am stuck trying to get the child Create working. For consistency with other Django examples, take the parent to be Author and the child to be Book. What is the simplest way to allow users to add Books to an Author?

In HTML terms, I think that I want to make a link on the Author detail page that includes the ID of the Author, have that ID be pre-set on the Book form, and then have the Book form processing use that ID as the PK of the Book. But I don't understand how to use Django to make this happen. I have read through https://docs.djangoproject.com/en/1.6/topics/class-based-views/generic-editing/, How do I use CreateView with a ModelForm, How do I set initial data on a Django class based generic createview with request data, and Set initial value to modelform in class based generic views, each of which seems to answer a slightly different question.

Here is the relevant code:

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'),

templates/myapp/author_detail.html

...
<p><a href="{% url 'myapp:book_create' author_id=Author.pk %}">Add a book</a></p>
...

templates/myapp/book_form.html

<form action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Done">
</form>

Questions

1) How do I get the Author ID from the Book page URL to the Author form, and then processed correctly? With the sample code above, the Django debugger shows that it's present in this way:

View function            Arguments      Keyword arguments         URL name
myapp.views.BookCreate   ()             {'author_id': u'1234'}    book_create

but I don't understand how to grab that variable out of the ... context? ... and put it into the form.

1a) Can I make it a url parameter instead of part of the URL itself, i.e., book/create?author=1234 instead of book/create/1234/? Or even make the whole thing a POST so that it's not part of the URL? Which is the best practice, and how is it done?

2) Once the variable is in the form, how can it be present as a hidden input, so that the user doesn't have to see it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

With the url that you defined in author_detail.html the author_id variable will be accessible in the view as 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

Then in your form you can add:

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)

Then in the template:

  <input type=hidden name="author_id" value="{{ a_id }}">

The form_valid in the view should retrieve the id, get the appropriate author and set that author as the books author. The commit=False prevents the model getting saved at first while you set the author and calling super will result in form.save(commit=True) being called.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...