Based on my django project I want to add multiple users to a group and I'm trying to do this using a form. But when I try adding choices via CheckBoxSelectMultiple() widget its not working as expected.
models.py file
from django.db import models
from members.models import User
from django.contrib.auth.models import Group
class Sprint(models.Model):
status_choice = [('Working','Working'), ('Closed','Closed')]
sprint_name = models.CharField(max_length=180)
sprint_created = models.DateTimeField(auto_now= True)
lead = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='team_lead')
sprint_members = models.ForeignKey(Group, on_delete=models.CASCADE, related_name = "member")
sprint_period = models.PositiveIntegerField()
sprint_status = models.CharField(max_length=10, choices=status_choice, default='Working')
forms.py file
from django import forms
from .models import Sprint
from members.models import User
from django.core.exceptions import ValidationError
from django.contrib.auth.models import Group
class SprintForm(forms.Form):
sprint_name = forms.CharField()
sprint_period = forms.IntegerField(label="Sprint period (in days)", min_value=7)
lead = forms.ModelChoiceField(queryset= User.objects.filter(role = 'Developer'),
label="Team Lead")
sprint_members = forms.ModelChoiceField(queryset= User.objects.filter(role =
'Developer'), widget= forms.CheckboxSelectMultiple(), label="Team members")
# class Meta:
# model = Sprint
# fields = ['sprint_name', 'lead', 'sprint_period', 'sprint_members']
def clean(self):
cleaned_data = super().clean()
lead = cleaned_data.get('lead')
team = cleaned_data.get('sprint_members')
if team and lead:
if lead in team:
raise ValidationError('Team lead cant be in team')
def save(self):
team = Group.objects.get_or_create(name=f'team{self.cleaned_data["sprint_name"]}')[0]
for team_member in self.cleaned_data['sprint_members']:
team.user_set.add(team_member)
Sprint.objects.update_or_create(
sprint_name=self.cleaned_data["sprint_name"],
lead=self.cleaned_data["lead"],
sprint_period=self.cleaned_data["sprint_period"],
_members=team
)
views.py file
class SprintCreationView(generic.FormView):
model = Sprint
template_name = 'sprint_creation.html'
form_class = SprintForm
def form_valid(self, form):
form.save()
return super().form_valid(form)
When trying to submit the form it is showing "Select a valid choice. That choice is not one of the available choices."
Related
Just quickly to summarize I have an app, which allows clubs to sign up and carry out different tasks. One of the features is a scheduling / rosters. Currently I have a form to add times to the roster. The club pages work off a session key based on the initially selected club.
My form consists of:
club_id - which I have hidden and initialized based on the logged in user.
pitch_id - which is currently displaying all pitches associated to all clubs but I need this to only show pitches based on the foreign key for the club_id
Would appreciate any help.
form.py
from django import forms
from clubkit.roster.models import RosterId
import datetime
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
class RosterForm(forms.ModelForm):
class Meta():
model = RosterId
fields = ('club_id', 'pitch_id', 'team_id', 'date',
'start_time', 'finish_time', 'reoccuring_event',)
widgets = {
'date': forms.DateInput(attrs={'id': 'datepicker'})
}
def clean_date(self):
date = self.clean_date['date']
if date < datetime.date.today():
raise ValidationError(_('Date cannot be in the past.'))
return date
def __init__(self, *args, **kwargs):
super(RosterForm, self).__init__(*args, **kwargs)
self.fields['club_id'].widget = forms.HiddenInput()
models.py for pitch
class Pitch(models.Model):
club_id = models.ForeignKey(ClubInfo, on_delete=models.CASCADE, related_name="pitches")
pitch_name = models.CharField(max_length=30)
PITCH_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
PITCH_TYPE = (
('1', 'Outdoor'),
('2', 'Indoor'),
)
pitch_size = models.CharField(max_length=1, choices=PITCH_SIZES)
pitch_type = models.CharField(max_length=1, choices=PITCH_TYPE)
open_time = models.TimeField(default='09:00')
close_time = models.TimeField(default='22:00')
RENT_TYPE = (
('0', 'Not Available To Rent'),
('1', 'Available To Rent'),
)
rental = models.CharField(max_length=1, choices=RENT_TYPE)
rental_price = models.DecimalField(default=0.00, max_digits=6, decimal_places=2)
max_people = models.IntegerField(null=True)
def __str__(self):
return self.pitch_name
models.py for roster
from django.db import models
from clubkit.clubs.models import ClubInfo, Pitch, Team
# Model to store roster information
class RosterId(models.Model):
club_id = models.ForeignKey(ClubInfo, on_delete=models.CASCADE)
pitch_id = models.ForeignKey(Pitch, on_delete=models.CASCADE)
team_id = models.ForeignKey(Team, on_delete=models.CASCADE)
date = models.DateField(max_length=8)
start_time = models.TimeField(default='')
finish_time = models.TimeField(default='')
reoccuring_event = models.BooleanField(default=False)
views.py
class ClubRoster(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'roster.html'
# Get method to retrieve current roster information and form
def get(self, request):
if request.user.is_authenticated:
club_pk = request.session.get('pk')
club_info = ClubInfo.objects.filter(user=request.user).first()
reoccuring_event = RosterId.objects.filter(reoccuring_event=True, club_id=club_pk)
inital_data = {
'club_id': club_info,
}
form = RosterForm(initial=inital_data)
roster = RosterId.objects.filter(club_id=club_pk)
return Response({'form': form,
'roster': roster,
'club_pk': club_pk,
'reoccuring_event': reoccuring_event
})
Sounds like you're looking for some manipulation of the form field query. Maybe this answer will provide further help.
The result could look like this.
from django import forms
from clubkit.roster.models import RosterId, Pitch
import datetime
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
class RosterForm(forms.ModelForm):
class Meta():
model = RosterId
fields = ('club_id', 'pitch_id', 'team_id', 'date',
'start_time', 'finish_time', 'reoccuring_event',)
widgets = {
'date': forms.DateInput(attrs={'id': 'datepicker'})
}
def clean_date(self):
date = self.clean_date['date']
if date < datetime.date.today():
raise ValidationError(_('Date cannot be in the past.'))
return date
def __init__(self, *args, **kwargs):
super(RosterForm, self).__init__(*args, **kwargs)
self.fields['club_id'].widget = forms.HiddenInput()
instance = kwargs.get('instance', None)
if instance:
self.fields['pitch_id'].queryset = Roster.objects.filter(club_id=instance.club_id)
Ah well or adjust instance to your 'initial'.
All other data is saved ideally but as shown below, the user id part shows as a pull down bar and a null value which should be a signed-in username.
What's wrong with my code?
The database page
Here's my code.
views.py
from .models import Markers
from .forms import AddMarkersInfo
from django.http import HttpResponse
def addinfo(request):
if request.method == 'POST':
mks = AddMarkersInfo(request.POST)
if mks.is_valid():
submit = mks.save(commit=False)
submit.user = request.user
submit.save()
name = mks.cleaned_data['name']
address = mks.cleaned_data['address']
description = mks.cleaned_data['description']
type = mks.cleaned_data['type']
lat = mks.cleaned_data['lat']
lng = mks.cleaned_data['lng']
Markers.objects.get_or_create(name=name, address=address, description=description, type=type, lat=lat, lng=lng)
return render(request, 'home.html', {'mks': mks })
else:
mks = AddMarkersInfo()
return render(request, 'home.html', {'mks': mks})
models.py
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from django.contrib.auth import get_user_model
def get_sentinel_user():
return get_user_model().objects.get_or_create(username='deleted')[0]
class Markers(models.Model):
User = settings.AUTH_USER_MODEL
use_id= models.ForeignKey(User, null=True, on_delete=models.SET(get_sentinel_user),)
name = models.CharField(max_length=60,default = 'name')
address = models.CharField(max_length=100,default = 'address')
description = models.CharField(max_length=150, default='description')
types = (
('m', 'museum'),
('s', 'school'),
('r', 'restaurant'),
('o', 'other'),
)
type = models.CharField(max_length=60, choices=types, default='museum')
lat = models.IntegerField()
lng = models.IntegerField()
forms.py
from django import forms
from maps.models import Markers
class AddMarkersInfo(forms.ModelForm):
class Meta:
model = Markers
fields = ['name','address','description', 'type','lat','lng',]
Well, first of all, you should remove the lines from django.contrib.auth.models import User and User = settings.AUTH_USER_MODEL in models.py if you are going to use settings.AUTH_USER_MODEL. You should use only one of the two.
And you can change your field to:
use_id= models.ForeignKey(settings.AUTH_USER_MODEL, ...
Secondly, it seems like you are duplicating the creation. The lines
submit = mks.save(commit=False)
submit.user = request.user
submit.save()
already create an Markers instance, so there is no need to call Markers.objects.get_or_create(... after that.
And, according to you models, the field should be submit.use_id instead of submit.user.
Now, if I understand your question correctly you want to make the use_id field read-only in your form/template.
I don't know why that field is even showing up in your form, since it is not listed in your forms Meta.fields.
You could try something like setting the widget attribute readonly:
class AddMarkersInfo(forms.ModelForm):
class Meta:
model = Markers
fields = ['use_id', 'name', 'address', 'description', 'type', 'lat', 'lng']
widgets = {
'use_id': forms.Textarea(attrs={'readonly': 'readonly'}),
}
I want to create a messaging function in ma django app. User should be able to write other users a textmessage.
models.py
from django.contrib.auth.models import User
class Message(models.Model):
recipient = models.ForeignKey(User, null=True)
contentDescription = models.CharField(max_length=1000, null=True)
By default, with no forms.py entry I get a selection, which will be unuseful with many users. I want the message sender to type in the user name, or in the first step the user id (which I could resolve with ajax from the name) .
Integer
But with forms.py
recipient = forms.IntegerField( widget=forms.NumberInput , required=False,)
I get:
Cannot assign "11": "Transport.recipient" must be a "User" instance.
ChoiceField and NumberInput
with:
recipient = forms.ChoiceField( widget=forms.NumberInput, required=False,)
I get the error message "not valid"
Is it possible to write the foreignkey 'manually' at all?
Try this:
recipient = forms.ModelChoiceField(queryset=User.objects.all(), widget=forms.Select, required=False)
considering your
models.py -
from django.contrib.auth.models import User
class Message(models.Model):
recipient = models.ManytoMany(User, null=True)
contentDescription = models.TextField()
forms.py
from .models import Message
from django import forms
from django.contrib.auth.models import User
class MailForm(forms.ModelForm):
recipient = forms.Charfield()
class Meta:
model = Message
fields = ('contentDescription',)
def clean_recipient(self):
user_list = self.cleaned_data.get('recipient')
# considering you post user_list of usernames as 'username1,username2,username3'
if user_list is not None:
user_list = user_list.split(',')
user_qs = User.objects.filter(username__in=userlist)
else:
raise forms.ValidationError('Error in this field')
return user_qs
def save(self, user_qs):
self.instance.user = user_qs
return super().save()
in views.py -
from .forms import MailForm
def your_view(request):
form = MailForm(request.POST or None)
if form.is_valid():
user_qs=form.cleaned_data.get('recipient')
form.save(user_qs)
#return render here
else:
#create your context here and return render
This is not perfect but can give you an idea how to implement. With the details you gave this is the best I can do for now.
I have exhausted all avenues in trying to put together a solution for this, but my current knowledge of Python and Django can only get me so far.
I'm creating a basic ticketing system and CreateView used to work until I created a Profile model and then separated the Ticket model into its own app. There were already a couple of tickets created when I refactored my code which is why I know ListView works, DeleteView works as well as DetailView. CreateView works until I hit the save button.
My views and models are below; I hope someone can please help me sort this out.
Ticket Model
from django.db import models
from django.contrib.auth.models import User
....
from qcapp.models import Profile
class Ticket(models.Model):
# Relations
user = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name="tickets", verbose_name="user")
# Attributes
title = models.CharField(max_length=250, verbose_name="Title", help_text="Enter a Ticket Title")
color = models.CharField(max_length=7,
default="#ffffff",
validators=[RegexValidator("(^#[0-9a-fA-F]{3}$)|(^#[0-9a-fA-F]{6}$)")],
verbose_name="Color",
help_text="Enter the hex color code, like #ccc or #cccccc")
description = models.TextField(max_length=1000)
created_date = models.DateTimeField(default=timezone.now, verbose_name='Created Date')
created_by = models.ForeignKey(User, related_name='created_by_user')
# Attributes
# Object Manager
objects = managers.ProjectManager()
# Meta and String
class Meta:
verbose_name = "Ticket"
verbose_name_plural = "Tickets"
ordering = ("user", "title")
unique_together = ("user", "title")
def __str__(self):
return "%s - %s" % (self.user, self.title)
def get_absolute_url(self):
return reverse('ticket_detail', args=[str(self.id)])
Ticket View (CreateView Only)
# -*- coding: utf-8 -*-
...
from django.views.generic import CreateView, UpdateView, DeleteView
...
from .models import Ticket
...
class TicketCreate(CreateView):
model = Ticket
template_name = "tickets/ticket_form.html"
fields = ['title', 'description']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super(TicketCreate, self).form_valid(form)
...
Profile Model(Imported Into Ticket Model)
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
from . import managers
class Profile(models.Model):
# Relations
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile", verbose_name="user")
# Attributes
interaction = models.PositiveIntegerField(default=0, verbose_name="interaction")
# Attributes
# Object Manager
objects = managers.ProfileManager()
# Custom Properties
#property
def username(self):
return self.user.username
# Methods
# Meta and String
class Meta:
verbose_name = "Profile"
verbose_name_plural = "Profiles"
ordering = ("user",)
def __str__(self):
return self.user.username
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
It looks like you need to add the following to your TicketCreate class in the form_valid function:
form.instance.user = Profile.objects.get(user=self.request.user)
Let me know if that works!
I have 2 class from my model
class APPLICANT_DATA(models.Model):
FIRST_NAME= models.CharField(max_length=20)
LAST_NAME= models.CharField(max_length=20)
MIDDLE_NAME= models.CharField(max_length=20)
and
class Applicant_status(models.Model):
fkey = models.ForeignKey(APPLICANT_DATA)
COMMENTS = models.CharField(max_length=100, null=True)
date_of_app = models.DateTimeField(auto_now_add=True, blank=True)
how do i make my 'Applicant_status' to populate whenever a data in 'APPLICANT_DATA' is inserted?
Here is my views.py
def save_page(request):
form = application_form(request.POST)
if request.method == 'POST':
if form.is_valid():
emails = form.cleaned_data['EMAIL']
mail = EmailMessage("Your activation and application code is: asdasd, do not show this to anyone", to=[emails])
mail.send()
cde = form.save(commit=False)
applicant_status.objects.create(fk=cde.id)
cde.save()
return HttpResponseRedirect('verify')
else:
form = application_form()
return render(request, 'frontend/apply.html', {'form': form})`
EDIT: My forms.py
from django.forms import ModelForm
from django import forms
from .models import APPLICANT_DATA
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class application_form(ModelForm):
FIRST_NAME = forms.CharField( max_length=20, label = ("First Name"),
. . .
class Meta:
model = APPLICANT_DATA
fields = ('FIRST_NAME', 'LAST_NAME', 'MIDDLE_NAME', )
error is (1048, "Column 'fkey' cannot be null")
Sorry if this was a newbie question
Use django signals to achieve this. https://docs.djangoproject.com/en/1.8/topics/signals/
Or you can manually save Applicant_Status with fk set to Application_Data you have just received in your view.