I'm new to Django (1.9.6) and I'm trying to understand if it is possible to validate a field on a ModelForm that needs to reference information that is contained in a Foreign Key from the referenced model.
How can I validate that the value a user inputs for "num_tickets" on the OrderForm is less than or equal to the "tickets_remaining" field on the Event class which is connected through a foreign key relationship?
I don't want to expose the Event field from the Order class on the OrderForm as the user has already accessed the specific event page, and has already selected to purchase tickets.
Models.py
class Order(models.Model):
first_name = models.CharField('First Name', max_length=120,null=False, blank=False)
last_name = models.CharField('Last Name', max_length=120, null=False, blank=False)
email = models.EmailField('Email', null=False, blank=False)
event = models.ForeignKey(Event)
num_tickets = models.PositiveIntegerField('Tickets', null=False, blank=False, validators=[MinValueValidator(0)])
total_price = models.DecimalField('Total', max_digits=8, decimal_places=2, default=0.0)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
class Event(models.Model):
event_name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=8, decimal_places=2, default=00.00, validators=[MinValueValidator(0)])
tickets_remaining = models.PositiveIntegerField(default=300)
Forms.py
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['first_name', 'last_name', 'email', 'num_tickets']
def clean_num_tickets(self):
tickets = self.cleaned_data["num_tickets"]
# validation Logic. Want to ensure a user cannot purchase more
# tickets than what an event has for "tickets_remaining"
return tickets
You don't show how you're associating the order with the event in the first place. If you haven't done that, then your problem is wider than just validating the tickets available.
I would recommend passing that Event from the view into the form instantiation. You can then use it both to associate the order with that event, and to validate the tickets.
class OrderForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event', None)
super(OrderForm, self).__init__(*args, **kwargs)
def clean_num_tickets(self):
tickets = self.cleaned_data["num_tickets"]
if tickets > self.event.tickets_remaining:
raise ValidationError('Too many tickets')
return tickets
def save(self, commit=False):
order = super(OrderForm, self).save(commit=False)
order.event = self.event
if commit:
order.save()
return commit
Now pass the event into the form when instantiating it:
form = OrderForm(request.POST, event=event)
Related
I have a form for filling out lessons that I want to limit who the students can select as their teacher to only confirmed connections. I have three models:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=254, null=True, blank=True)
class Lesson(models.Model):
user = models.ForeignKey(User, related_name='fencer', on_delete=models.SET_NULL, null=True, blank=True)
teacher = models.ForeignKey(Fencer, related_name='instructor', on_delete=models.SET_NULL, null=True, blank=True)
lesson_date = models.DateField(default="1900-01-01")
title = models.CharField(max_length=100, null = True, blank=True)
description = models.TextField(null=True, blank=True)
class Connection(models.Model):
student = models.ForeignKey(User, related_name='student', on_delete=models.CASCADE, blank=True)
teacher = models.ForeignKey(User, related_name='teacher', on_delete=models.CASCADE, blank=True)
student_accepts = models.BooleanField(default=False)
teacher_accepts = models.BooleanField(default=False)
#property
def connected(self):
if self.student_accepts == True and self.teacher_accepts == True:
return True
else:
return False
My form so far is:
class LessonForm(ModelForm):
class Meta:
model = models.Lesson
#fields = ()
fields = '__all__'
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['teacher'].queryset = Users.objects.filter() # the best I have so far
How do I filter the User model based on the link made in the Connection model? Maybe I'm overcomplicating this or is there a better way?
Thank you in advance
Found the answer in this other question on here about spanning models.
I had a hard time getting django to see the fields for some reason so I will keep this ugly version.
def __init__(self, user, *args, **kwargs): # will need to pass the user to the form when used
super(LessonForm, self).__init__(*args, **kwargs)
# reduce options to just the coaches that the student is connected with
connected_teachers = Connection.objects.filter(Q(student=user) and (Q(student_accepts=True) and Q(teacher_accepts=True)))
teachers = User.objects.filter(teacher__in=connected_teachers)
self.fields['teacher'].queryset = teachers
I am facing one issue with django forms
Here is my model :
class User(models.Model):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class UserProfile(AuditFields):
user = models.ForeignKey(User, on_delete=models.CASCADE)
designation = models.CharField(max_length=200, blank=True)
contact_number = models.CharField(max_length=20, blank=True)
team = models.CharField(max_length=200, blank=True)
manager = models.CharField(max_length=200, blank=True)
joining_date = models.DateField(default=datetime.now)
I need to create a form for editing profile details of the current user
This is my form. But it is a model Form so only getting the detauls from the User Profile table only
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
exclude = ['user']
How can I get first_name , last_name from User table and save it
Just add the fields as a CharField in form, and use cleaned_data attribute to fetch the data and save it:
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(max_length=30,required=True)
last_name = forms.CharField(max_length=30,required=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
class Meta:
model = UserProfile
exclude = ['user']
def save(self, commit=False):
instance = super().save(commit=True)
user = instance.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return instance
Ive spent a fair bit of time searching on this subject without finding some real up to date answers. I'm trying to create a form that creates a db entry. The basic idea is this:
Many events can have many people
So, the struggle here is that the user needs to create an event where the user can select all the people that attend. Each person that attends though, has certain things that also needs to be tracked per event. See the model below:
models.py
from django.db import models
from django.contrib.auth.models import User[]
class PersonRole(models.Model):
role = models.CharField(max_length=20, choices=ROLE_CHOICES, unique=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.role
class PersonClass(models.Model):
name = models.CharField(max_length=100, choices=CLASS_CHOICES, unique=True)
color = models.CharField(max_length=6, choices=COLOR_CHOICES, unique=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
class Person(models.Model):
name = models.CharField(max_length=100, unique=True)
personclass = models.ForeignKey(PersonClass, on_delete=models.CASCADE, blank=True, null=True)
personrole = models.ForeignKey(PersonRole, on_delete=models.CASCADE, blank=True, null=True)
value = models.IntegerField(default=0)
reliability = models.IntegerField(default=0)
last_item = models.DateField(auto_now=False, blank=True, null=True)
last_event_attended = models.DateField(auto_now=False, blank=True, null=True)
last_manager_attended = models.DateField(auto_now=False, blank=True, null=True)
item_received = models.BooleanField(default=False)
note = models.TextField(null=True, blank=True)
core_attendee = models.BooleanField(default=False)
enabled = models.BooleanField(default=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
class Location(models.Model):
name = models.CharField(max_length=100, unique=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
class Boss(models.Model):
name = models.CharField(max_length=100, unique=True)
location = models.ForeignKey(Location, on_delete=models.CASCADE)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
class Raid(models.Model):
date = models.DateTimeField(auto_now_add=True)
boss = models.ForeignKey(Boss, on_delete=models.CASCADE, null=True, blank=True)
success = models.BooleanField()
attendees = models.ManyToManyField(Person)
created_by = models.ForeignKey(User,
related_name="raids", blank=True, null=True,
on_delete=models.SET_NULL)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return str(self.date)
I've started down the path of just trying to use the generic in-built create\update\delete views and ran into this:
ValueError: 'roster.Person' has no ForeignKey to 'roster.Raid'.
forms.py
class RaidGenericCreateModelForm(forms.ModelForm):
class Meta:
model = Person
exclude = ()
RaidPersonFormSet = inlineformset_factory(Raid, Person, fields=['name', 'personclass', 'personrole', 'item_received'], extra=1, can_delete=False)
views.py
class RaidCreate(CreateView):
model = Raid
template_name = 'roster/raid_create.html'
form_class = RaidGenericCreateModelForm
success_url = None
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
person_form = RaidPersonFormSet
return self.render_to_response(
self.get_context_data(form=form,
person_form=person_form
)
)
There are 9-year old posts that say you cannot use inlineformset_factory with many to many fields. So my question here is, what are my options? What is the best way to go about simply creating an Event (referred to as Raid in the model) and at the same time selecting the people from the roster (referred to as Person in the model) and changing the options those people have associated to them for that event?
As an example of what I am trying to accomplish here:
Event 1
-Person A (selected, item_received=True)
-Person B (selected, item_received=False)
-Person C (selected, item_received=False)
-Person D (not selected, item_received=False)
Event 2
-Person A (selected, item_received=False)
-Person B (not selected, item_received=False)
-Person C (selected, item_received=True)
-Person D (selected, item_received=False)
Where the list of persons is showing all persons and some of the persons fields from the Person model.
The alternate thing you can do is use DjangoRestFramework for this purpose.
Using rest you can first send persons data to frontend then in frontend you can create Event and add person details for each event,and in last post all that data using javascript.Try it,it will surely work.
I have a problem, I try to save the model and only adds to 'members' the users that belong to the company set in the field 'company'.
This is my code:
class GroupFolderAccess(BaseModel):
name = models.CharField(max_length=128)
members = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='belongs')
company = models.ForeignKey('Company', on_delete=models.CASCADE, related_name='folders')
folder = models.ForeignKey('recourse.Folder', null=True, blank=True, on_delete=models.CASCADE, related_name='get_group')
def save(self, *args, **kwargs):
for member in self.members.all():
if self.company != member.company:
print(member)
self.members.remove(member)
return super(GroupFolderAccess, self).save(*args, **kwargs)
When I save, it displays users correctly, but does not remove them from the relationship.
I want to create an inline formset between Preorder model and Product model. The scenario is that the user will be able to select one or more than one products when he decides to create a preorder. On the other hand a product might be found in one or more than one preorders. With that in mind i created a manytomany relationship.
models.py
class Preorder(models.Model):
client = models.ForeignKey(Client,verbose_name=u'Client')
invoice_date = models.DateField("Invoice date",null=True, blank=True, default=datetime.date.today)
preorder_has_products = models.ManyToManyField(Product, blank=True)
def get_absolute_url(self):
return reverse('preorder_edit', kwargs={'pk': self.pk})
class Product(models.Model):
name = models.CharField("Name",max_length=200)
price = models.DecimalField("Price", max_digits=7, decimal_places=2, default=0)
barcode = models.CharField(max_length=16, blank=True, default="")
eopyy = models.CharField("Code eoppy",max_length=10, blank=True, default="")
fpa = models.ForeignKey(FPA, null=True, blank=True, verbose_name=u'Fpa Scale')
forms.py
class PreorderForm(ModelForm):
class Meta:
model = Preorder
exclude = ('client','preorder_has_products',)
def __init__(self, *args, **kwargs):
super(PreorderForm, self).__init__(*args,**kwargs)
self.fields['invoice_date'].widget = MyDateInput(attrs={'class':'date'})
class ProductForm(ModelForm):
#name = ModelChoiceField(required=True,queryset=Product.objects.all(),widget=autocomplete.ModelSelect2(url='name-autocomplete'))
class Meta:
model=Product
fields = '__all__'
def __init__(self, *args, **kwargs):
super(ProductForm, self).__init__(*args, **kwargs)
self.fields['name'].label="Name"
self.fields['price'].label="Price"
and finally the inline formset:
PreorderProductFormSet = inlineformset_factory(Preorder, Product,
form=ProductForm, extra=1)
After run I face up the issue:ValueError at /
'intranet.Product' has no ForeignKey to 'intranet.Preorder'
Why this happening since I created a manytomany relation?
One solution is to create a foreign key relationship between Preorder and Product model inside Product model..but I do not want to do that since product model is used in other areas of my project and do not want to mess it up.
Any suggestions?