简体   繁体   中英

Django ModelForm foreign key filtering

I'm trying to filter foreign key field selections in my model form, but form isn't working. My scripts:

forms.py

from django import forms
from .models import Album, Song

class SongCreateForm(forms.ModelForm):

    class Meta:
        model = Song
        fields = [
            'album',
            'name',
            'is_favorite'
        ]
        widgets = {
            'album': forms.Select(attrs={'class': 'form-control'}),
            'name': forms.TextInput(attrs={'class': 'form-control'}),
            'is_favorite': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
        }

    def __init__(self, user, *args, **kwargs):
        super(SongCreateForm, self).__init__(*args, **kwargs)
        self.fields['album'].queryset = Album.objects.filter(owner=user)

views.py

from django.views.generic import CreateView

class SongCreateView(CreateView):
    template_name = 'music/song_create.html'
    success_url = '/songs/'

    def get_form(self, form_class=None):
        form_class = SongCreateForm(user=self.request.user)
        return form_class
        print(form_class.errors.as_data())
        print(form_class.is_valid())

song_create.html

{% extends 'base.html' %}

{% block content %}
<form method="post">{% csrf_token %}
  {% if form.errors %}
    <p>{{ form.errors }}</p>
  {% endif %}
  <div class="form-group">
    <label for="{{ form.album.id_for_label }}">Album</label>
    {{ form.album }}
  </div>
  <div class="form-group">
    <label for="{{ form.name.id_for_label }}">Name</label>
    {{ form.name }}
  </div>
  <div class="form-check">
    <label for="{{ form.is_favorite.id_for_label }}" class="form-check-label">
    {{ form.is_favorite }}Favorite
    </label>
  </div>
  <button type="submit" class="btn btn-primary" value="Save">Add</button>
</form>
{% endblock %}

Filtering queryset of 'album' field is working correctly. It is displaying only albums that are associated with authenticated user, but when I click submit in form browser doesn't redirect me to success url and song isn't added to database. I've added two print statements at the end of views.py to check that form is valid and has any errors. While form.errors returning empty dictionary , form.is_valid() is equal to False . Why Django treats this form as invalid, if it doesn't have any errors?

Note: SongCreateView works successfully, when I comment out init function in forms.py and get_form function in views.py.

First of all, remove those print statements after the return statement as they will never get executed after a value is returned by the method. Secondly, add form_class attribute to your view class like this:

class SongCreateView(CreateView):
    template_name = 'music/song_create.html'
    success_url = '/songs/'
    form_class = SongCreateForm

Then in get_form method:

def get_form(self, form_class=None):
    if form_class is None:
        form_class = self.get_form_class()
    return form_class(user=self.request.user, **self.get_form_kwargs())

Then override form_valid method to associate the logged in user with the Song instance like this:

def form_valid(self, form):
    self.object = form.save(commit=False)
    self.object.user = self.request.user
    self.object.save()
    return redirect(self.get_success_url())

Import redirect from django.shortcuts at the top. Hope it helps!

My guess is that the ID of Song is missing. When you use the magic bit of code

{{ form }}

this field is added automatically, although the input is hidden. When you define all fields manually like you are doing you will have to make sure the ID is added, that is you need to add

{{ form.pk }}

or whatever the name (here pk) of the id of your song is.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM