38

I'm making a game link site, where users can post links to their favorite web game. When people post games they are supposed to check what category the game falls into. I decided to allow many categories for each game since some games can fall into many categories. So the question is, how do I handle this in my view? And how can I show it as Checkboxes, where at least one has to be checked? And how can I show this as checkboxes in the Admin as well?

Here is the code

Models:

class Category(models.Model): 
        category = models.CharField(max_length=200) 
        def __unicode__(self): 
                return self.category 
class Game(models.Model): 
    name = models.CharField(max_length=200) 
    url = models.CharField(max_length=200) 
    poster = models.ForeignKey(User, related_name='game_poster_set') 
    postdate = models.DateTimeField(default=datetime.now) 
    cats = models.ManyToManyField(Category) 
    hits = models.IntegerField(default=0) 
    post = models.BooleanField(default=False) 

Views:

def submit(request): 
        form = GameForm(request.POST or None) 
        if form.is_valid(): 
                game = form.save(commit=False) 
                game.poster = request.user 
                game.save() 
                next = reverse('gamesite.games.views.favorites') 
                return HttpResponseRedirect(next) 
        return render_to_response( 
        'games/submit.html', 
        {'form': form}, 
        context_instance = RequestContext(request),)

Forms:

class GameForm(forms.ModelForm): 
        name = forms.CharField(max_length=15, label='Name') 
        url = forms.URLField(label='URL', initial='http://') 
        class Meta: 
                model = Game 
                fields = ('name','url')

Thanks!

Vebjorn Ljosa
  • 17,438
  • 13
  • 70
  • 88
Grétar Jónsson
  • 843
  • 1
  • 7
  • 8

3 Answers3

51
class GameForm(forms.ModelForm): 
        name = forms.CharField(max_length=15, label='Name') 
        url = forms.URLField(label='URL', initial='http://') 
        cats = forms.ModelMultipleChoiceField(
            queryset=Category.objects.all(),
            widget=forms.CheckboxSelectMultiple,
            required=True)

        class Meta: 
                model = Game 
                fields = ('name','url','cats')

that should fix your view, but i'm not sure about the admin. still looking... will edit if i find anything.

Antoine Pinsard
  • 33,148
  • 8
  • 67
  • 87
Brandon Henry
  • 3,632
  • 2
  • 25
  • 30
  • this isn't exactly what you're trying to do, but i don't have the time to convert it right now. try to manipulate this link to match what you're trying to do. http://www.kryogenix.org/days/2008/03/28/overriding-a-single-field-in-the-django-admin-using-newforms-admin – Brandon Henry Nov 19 '09 at 02:25
  • 8
    I couldn't get this working in django 1.3, but the following should work: cats = forms.ModelMultipleChoiceField(queryset=Category.objects.all(),widget=forms.CheckboxSelectMultiple(),required=True) – shawnwall Jul 07 '11 at 18:38
  • 1
    TypeError: __init__() missing 1 required positional argument: 'queryset' – Rajiv Sharma Dec 20 '17 at 02:31
46

Here is how I solved it (Edit: and the admin thing)

Forms:

cats = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Category.objects.all())

(It was the queryset part I couldn't find..)

View:

cats = form.cleaned_data['cats']
    game.cats = cats

And that's all the code needed to save the data.

Edit: here is a solution for the admin

Models:

from django.contrib import admin
from django.forms import CheckboxSelectMultiple

class MyModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.ManyToManyField: {'widget': CheckboxSelectMultiple},
    }

Admin:

from gamesite.games.models import Game, MyModelAdmin

admin.site.register(Game, MyModelAdmin)

It's kind of quirky in looks, but works! If someone finds a way to make it more "clean" please post!

Cheers!

Grétar Jónsson
  • 843
  • 1
  • 7
  • 8
  • oh duh! my bad for not putting the queryset part in my solution. – Brandon Henry Nov 20 '09 at 00:17
  • Thanks for your admin panel solution. I wonder if there's a way of choosing the sort order and integrating sorl-thumbnail into it. Any clues? – cwj Feb 27 '12 at 22:36
  • Hi, I used `Country` class instead of `Category` & in template file I generated the checkboxes using `{{ form.countries }}`, it shows the checkboxes but instead of any field in the Country class it shows `Country object`. Could you please help? – sha256 May 01 '13 at 11:02
  • As this works flawless, I do have a question, is there any way I can choose which fields I want to display from the Category model in my template? – Yorbjörn Aug 27 '21 at 12:57
4

Found this on from Chase Seibert, Engineering Manager of Dropbox

Source from Chase Seibert

from django.db import models
from django.forms.models import ModelForm
from django.forms.widgets import CheckboxSelectMultiple

class Company(models.Model):  
    industries = models.ManyToManyField(Industry, blank=True, null=True)

class CompanyForm(ModelForm):

    class Meta:
        model = Company
        fields = ("industries")

    def __init__(self, *args, **kwargs):

        super(CompanyForm, self).__init__(*args, **kwargs)

        self.fields["industries"].widget = CheckboxSelectMultiple()
        self.fields["industries"].queryset = Industry.objects.all()