How can I render a ManyToManyField as checkboxes? - python

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!

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.

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!

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()

Related

Stuck on linking ManytoMany relationships with Modelform

I'm pretty new to Django and I am working on a project that currently requires the following:
I have two basic structures: a Project model and a TeamMember model- both related to each other through a ManytoMany relationship. Then I have an TMAssigned 'through' class. The team member will have many projects assigned to it over time.
I have a ModelFrom which creates a Project model through the creation of the form.
My question is, How do I link the team member to the newly created project upon the submission of the form?
Here is a bit of my model & form code:
TeamMember
class TeamMember(models.Model):
firstname = models.CharField(max_length=100, default= "First Name")
lastname = models.CharField(max_length=100, default= "Last Name")
fullname = models.CharField(max_length=100, default= "Full Name")
email = models.EmailField(max_length=254)
cellphone = PhoneNumberField(null=False, blank=False, unique=True)
numberofcases = models.IntegerField(max_length=10000, default=0)
#property
def fullnamefunc(self):
fullname = "{} {}".format(self.firstname, self.lastname)
return fullname
def __str__(self):
return self.fullname
Project
class Project(models.Model):
pursuitname = models.CharField(max_length=500)
datecreated = models.DateTimeField(auto_now=True)
bdmember = models.ManyToManyField('team.TeamMember')
Views.py
class bdFormView(TemplateView):
template_name = os.path.join(BASE_DIR, "templates/masterform/bdform.html")
def get(self,request):
form = bdForm()
return render (request, self.template_name, {'form': form})
def post(self, request):
form = bdForm(request.POST)
if form.is_valid():
print("form is valid")
project = form.save(commit=False)
project.save()
text = form.cleaned_data['briefcard']
Form.py
class bdForm(forms.ModelForm):
bdmemberlist = TeamMember.objects.all().order_by('lastname')
pursuitname = forms.CharField()
bdmember = forms.ModelChoiceField(queryset= bdmemberlist)
addbdteam = forms.ModelMultipleChoiceField(
queryset=TeamMember.objects.all().order_by('lastname'), widget=Select2MultipleWidget, required=False)
class Meta:
model = Project
fields = ['pursuitname','addbdteam','bdmember',]
def __init__(self, *args, **kwargs):
if kwargs.get('instance'):
initial = kwargs.setdefault('initial', {})
initial['projects'] = [t.pk for t in
kwargs['instance'].project_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self, False)
old_save_m2m = self.save_m2m
def save_m2m():
old_save_m2m()
for project in self.cleaned_data['bdmember']:
instance.teammember_set.add(project)
Thanks in advance!!
Edit- after doing some more research, I've removed the "Through" model from the script and am trying to rely on the form.py save method to do the join. However, when I do this- the two are still not linking up properly.
Since only your admin (superusers?) will log in, you can start off by using the in-built Django Admin.
I would recommend this for you, at least for now, because you're a beginner and the Admin Form is stunningly simple to use. Then, you can create a custom form later on when you're more comfortable. :-)
With this in mind, you can try eliminating the 'through' table (you may need to reset your migrations), and try this.
Admin.py
from django.contrib import admin
from .models import TeamMember, TMAssigned, Project,
TeamMembersInLine(admin.TabularInline):
model = TeamMember
extra = 1
#admin.register(Project):
class ProjectAdmin(admin.ModelAdmin):
list_display = ('pursuitname', 'bdmember ', 'datecreated')
inlines = [TeamMembersInLine]
Here's another answer that delves into the through table. It was asked by someone in your situation and the answer is relevant too.

Filtering the options of ModelChoiceField in forms in django as per the ForeignKey relations

In my django project, I created three model classes. The class 'Subtopic' has ForeignKey relation with class 'Chapter' and class 'SAQ' has ForeignKey relation with class 'Chapter' and class 'Subtopic'.
#models.py
from django.db import models
class Chapter(models.Model):
chapter_serial = models.IntegerField(null=False, help_text="Chapter No.")
slug = models.SlugField(unique=True, blank=True)
chapter_name = models.CharField(max_length=120)
class Meta:
ordering = ["chapter_serial"]
def get_saq_list_url(self):
return reverse("cms:saq_list", kwargs={"chap_slug":self.slug})
class Subtopic(models.Model):
subtopic_serial = models.IntegerField(null=False)
title = models.CharField(max_length=240)
chapter = models.ForeignKey('Chapter', on_delete=models.CASCADE)
class Meta:
ordering = ["subtopic_serial"]
class SAQ(models.Model):
question_serial = models.IntegerField(null=False)
question = models.TextField()
answer = models.TextField()
chapter = models.ForeignKey('Chapter', on_delete=models.CASCADE)
subtopic = models.ForeignKey('Subtopic', on_delete=models.CASCADE, null=True, blank=True)
class Meta:
ordering = ["question_serial"]
I tried to create forms using django ModelForm for 'model SAQ' such that for each 'SAQ form' associated with a particular chapter instance, the choice field for model 'Subtopic' will contain only those subtopics of that particular chapter instance.
#forms.py
from django import forms
from .models import SAQ
class SAQForm(forms.ModelForm):
class Meta:
model = SAQ
fields = [
'question_serial',
'question',
'answer',
'important',
'remarks',
'subtopic',
]
The django view function to create the form is as below.
from django.shortcuts import render, get_object_or_404, redirect
from .models import SAQ, Chapter, Subtopic
from .forms import SAQForm
from django.http import HttpResponseRedirect
def saq_create(request, chap_slug=None):
chapter_instance = get_object_or_404(Chapter, slug=chap_slug)
form = SAQForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.chapter = chapter_instance
instance.save()
return HttpResponseRedirect(chapter_instance.get_saq_list_url())
context = {
"form":form,
"chapter_instance":chapter_instance,
}
return render(request, 'cms/saq_form.html', context)
With this configuration, the choice field in the form, for 'Subtopic' shows all subtopics for all chapter instances. Any suggestion would be very helpful.
I would suggest to override the form init and pass the chapter instance so you can filter the subtopic queryset.
Example (not tested, could contain typos):
#forms.py
from django import forms
from .models import SAQ
class SAQForm(forms.ModelForm):
class Meta:
model = SAQ
fields = [
'question_serial',
'question',
'answer',
'important',
'remarks',
'subtopic',
]
def __init__(self, chapter, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['subtopic'].queryset = \
self.fields['subtopic'].queryset.filter(chapter=chapter)
And then your view should pass the chapter instance to the form:
chapter_instance = get_object_or_404(Chapter, slug=chap_slug)
form = SAQForm(chapter_instance, request.POST or None)

Django filter by multipleChoiceField

I have model Skill
class Skill(models.Model):
hero= models.ForeignKey(Hero)
name = models.CharField(max_length=255)
And I have model Hero
class Hero(models.Model):
name = models.CharField(max_length=255)
I use multiple choice field to select skills
OPTIONS = (
("sharingan", "sharingan"),
("rasengan", "rasengan"),
("fireball", "fireball"),
)
skills= forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple(),
choices=OPTIONS)
I use get request to send my form and my search page url becomes something like:
mysite.com/search?skills=shiringan&skills=rasengan
In my views.py I have
def vip(request):
heroes = Hero.objects.all
return render(request, 'app_name/search.html',{'heroes': heroes})
What should I write in views.py to select all heroes with chosen skills?
You probably want to use ManyToManyField Instead.
like this:
# models.py
from django.db import models
class Skill(models.Model):
name = models.CharField(max_length=255)
class Hero(models.Model):
name = models.CharField(max_length=255)
skills = models.ManyToManyField(Skill)
and use forms like this:
# forms.py
from django import forms
from .models import Hero
class HeroForm(forms.ModelForm):
class Meta:
model = Hero
fields = '__all__'
and finally your views:
# views.py
from .models import Hero, Skill
from .forms import HeroForm
def my_heroes(request):
heroes = Hero.objects.all()
return render(request, 'your_template.html', {'heroes':heroes})
def hero_skill_select(request):
if request.method == 'POST':
form = HeroForm(request.POST)
# do something
else:
form = HeroForm()
return render(request, 'template_with_form.html', {'form':form})
Shouldn't the Hero model have Skill as foreignfield, rather than the opposite? I don't understand the logic behind your classes.
Based on your current Classes and their relationship, you could do something like this:
chosen_skills = Skill.objects.values('hero').distinct().annotate(skill='name', hero_name='hero__name')
Haven't tested it, but basically, for every distinct hero, it returns the skill and the name. Let me know if this helps.

django user authentication incorporated with models and views

I still cant seem to put a simple program together. I am thinking of a simple program where my coworkers and I could propose a bet with each other. (example: I bet the detriot Lions dont win another game this season) If someone is feeling up to the task of taking that bet they can just hit an accept button. I want the user to be logged in and upon submitting the bet_name and bet_desc of the bet, their username, bet_name, bet_desc, and timestamp is saved to a table. Then a relationship table would save the bet id, win, tie, created_user_id, accepted_user_id, accepted_timestamp, and cancelled. Then userprofile table to keep their username, wins, losses, ties, bets_made, and bets_accepted.
models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
class UserProfiles(models.Model):
username = models.OneToOneField(User)
wins = models.PositiveIntegerField(default=0)
losses = models.PositiveIntegerField(default=0)
ties = models.PositiveIntegerField(default=0)
bets_made = models.PositiveIntegerField(default=0)
bets_accepted = models.PositiveIntegerField(default=0)
def __str__(self):
return "%s's profile" % self.username
class Bets2(models.Model):
bet_name = models.CharField(max_length=30)
bet_desc = models.CharField(max_length=300)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
created_user = models.OneToOneField(UserProfiles, primary_key=True)
accepted_user = models.PositiveIntegerField(default=0)
accepted_timestamp = models.DateTimeField(auto_now=True)
win = models.BooleanField(default=True)
tie = models.BooleanField(default=True)
cancelled = models.BooleanField(default=True)
def __str__(self):
return "%s's profile" % self.bet_name
As far as forms.py and views this is all i have so far:
forms.py
from django import forms
from django.core.exceptions import ValidationError
from .models import Bets2, UserProfiles
class BetForm(forms.ModelForm):
class Meta:
model = Bets2
fields = ['bet_name', 'bet_desc',]
def clean_name(self):
bet_name = self.cleaned_data.get('bet_name')
#write code here
return name
def clean_bet(self):
bet_desc = self.cleaned_data.get('bet_desc')
#write code here
return bet
views.py
from django.shortcuts import render
from django.conf import settings
from .forms import BetForm
from .models import Bets2, UserProfiles
def home(request):
title = "2015 Boardbets"
queryset = Bets2.objects.all().order_by('timestamp')
queryuser = UserProfiles.objects.all()
context = {
"title": title,
"queryset" : queryset,
"queryuser" : queryuser,
}
return render(request, "home.html", context)
def boardbet(request):
form = BetForm(request.POST or None)
bet_variables = Bets2.objects.all()
context = {
"form" : form,
"bet_variables" : bet_variables,
}
if form.is_valid():
form.save()
return render(request, "bets.html", context)
This is where i get stumped, i dont know how to incorporate the other two models into the view so that all three tables get created and also how to reference them in the template.
Edit: changed it to one model for Bets

Django: Slugify Post Data

I'm trying to save some form data inputted by the user. I would like to slugify the "name" which was entered by the user, but dont want the slug field to show on the template that the user sees. I tried to do it manually with the sell function that you see below, but cant quite get it to work. I want to eventually save the slugified name into the Item model I have listed below. I'm sure there's a much smarter/simpler way than the sell function I'm currently using :P. Thanks in advance!
class Item(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=75)
slug = models.SlugField(max_length=50, unique=True)
is_active = models.BooleanField(default=True)
image = models.CharField(max_length=50)
price = models.DecimalField(max_digits=9, decimal_places=2)
quantity = models.IntegerField(default=1)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
shipping_price = models.DecimalField(decimal_places=2, max_digits=6)
categories = models.ManyToManyField(Category)
class AddItem(forms.ModelForm):
class Meta:
model = Item
exclude = ('user','slug','is_active',)
def sell(request):
if request.method == "POST":
form = AddItem(request.POST)
item = form.save(commit=False)
item.user = request.user
item.is_active = True
item.slug = slugify(form.name) **#not sure what this line should be?**
item.save()
if form.is_valid():
form.save()
return HttpResponseRedirect('thanks.html')
else:
url = urlresolvers.reverse('register')
return HttpResponseRedirect(url)
You can exclude slug from user form.
And slugify in pre_save signal.
from django.dispatch import receiver
from django.db.models.signals import pre_save
#receiver(pre_save, sender=Item)
def iter_pre_save_handler(sender, instance, **kwargs):
if not instance.pk:
instance.slug = slugify(instance.name)
According to the docs, you can exclude a field from being rendered in a model form like this:
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title')
or
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
exclude = ('birth_date',)
or by setting editable=False on the Field instance in your model.
Once you have done this, you can override the save method of the model, as the comments in the OP have suggested:
# shamelessly copied from http://stackoverflow.com/questions/837828/how-do-i-create-a-slug-in-django/837835#837835
from django.template.defaultfilters import slugify
class test(models.Model):
q = models.CharField(max_length=30)
s = models.SlugField()
def save(self, *args, **kwargs):
self.s = slugify(self.q)
super(test, self).save(*args, **kwargs)

Categories

Resources