Steve Smith
Steve Smith

Reputation: 1089

Django Class Based View With ModelChoiceField

I've been working with Django for about 3 months now and feel I'm getting a bit better, working my way up to class based views. On the surface they seem cleaner and easier to understand and in some cases they are. In others, not so much. I am trying to use a simple drop down view via ModelChoiceField and a form. I can get it to work with a function based view as shown below in my views.py file:

def book_by_name(request):
    form = BookByName(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            book_byname = form.cleaned_data['dropdown']
            return HttpResponseRedirect(book_byname.get_absolute_url1())
    return render(request,'library/book_list.html',{'form':form})

Here is my form in forms.py:

class BookByName(forms.Form):

    dropdown = forms.ModelChoiceField(queryset=Book.objects.none())

    def __init__(self, *args, **kwargs):
        super(BookByName, self).__init__(*args, **kwargs)
        self.fields['dropdown'].widget.attrs['class'] = 'choices1'
        self.fields['dropdown'].empty_label = ''
        self.fields['dropdown'].queryset = Book.objects.order_by('publisher')

This code works. When I have tried to convert to a Class Based View, that's when the trouble begins. I tried to do something like this in views.py:

class BookByNameView(FormView, View):
    form_class = BookByName
    initial = { 'Book' : Book }
    template_name = 'library/book_list.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})

    def get_success_url(self, *args):
        return reverse_lazy('library:book_detail', args = (self.object.id,))

When using this with the same form, I receive an attribute error,

'BookByNameView' object has no attribute 'object'.

I've tried ListView as well and received several other errors along the way. The get_success_url also needs to take in a primary key and I can't figure out how to get that passed in as well. Again, I'm a 3 month Django newbie so please be gentle and thanks in advance for your thoughts and suggestions! I feel like I'm in the ballpark...just can't find my seat! I'm very open to doing this differently, if there's a cleaner/better way to do this!

Based on the latest feedback, it would appear the Class Based View should look like:

class BookNameView(FormView):
    form_class = BookName
    template_name = 'library/book_list.html'

    def get_success_url(self, *args):
        return reverse_lazy('library:book_detail')

Is this correct? I ran a test version of this and in response to your question as to why I am using self.object.id at all, I am trying to get the pk from the modelchoicefield that I am using to return the view I am trying to get. This may be where I am getting a bit lost. I am trying to get the detail view from the modelchoicefield dropdown, and return the book that is selected. However, I can't seem to pass the pk to this view successfully.

I updated my code to...

class BookByNameView(FormView, ListView):
    model = Book
    form_class = BookByName
    template_name = 'library/book_list.html'

    def get_success_url(self, *args):
        return reverse_lazy('library:book_detail')

But now it says error...Reverse for 'book_detail' with no arguments not found.

Upvotes: 1

Views: 683

Answers (2)

Ajmal Noushad
Ajmal Noushad

Reputation: 946

You can override the form_valid() function in FormView to achieve what you want. If the form is valid then it is passed to the form_valid() function.

Try this:

class BookByNameView(FormView):
       model = Book
       form_class = BookByName
       template_name = 'library/book_list.html'

       def form_valid(self, form):
            bookbyname = form.cleaned_data['dropdown']
            return HttpResponseRedirect(bookbyname.get_absolute_url())

Upvotes: 0

Daniel Roseman
Daniel Roseman

Reputation: 599590

Why are you using self.object there at all? You used form.cleaned_data in the original view, that's what you should use in the class based version too. Note that the form is passed to form_valid.

Note that you've done lots of other weird things too. Your getmethod is pointless, as is your definition of the initial dict; you should delete them both. Also, FormView already inherits from View, there's no need to have View in your declaration explicitly.

Upvotes: 1

Related Questions