Cannot assign must be a instance Django - django

I have an order form which returns this statement of submit:
Cannot assign "<Annual: 2012>": "Order.annuals" must be a "Catalog" instance.
I'm fairly new to Django. I understand it needs an instance instead of the string it has been passed. How would I go about resolving that?
Here is my view:
class OrderListCreateView(
views.LoginRequiredMixin,
views.SetHeadlineMixin,
generic.CreateView
):
form_class = forms.OrderListForm
headline = 'Create'
model = Order
template_name = 'ordercreate.html'
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super(OrderListCreateView, self).form_valid(form)
Here is my form:
class OrderListForm(forms.ModelForm):
annuals = forms.ModelChoiceField(queryset=Annual.objects.all())
issues = forms.ModelChoiceField(queryset=Issue.objects.all())
articles = forms.ModelChoiceField(queryset=Article.objects.all())
class Meta:
fields = (
'annuals',
'issues',
'articles',)
model = models.Order
def __init__(self, *args, **kwargs):
super(OrderListForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'annuals',
'issues',
'articles',
ButtonHolder(
Submit('create', 'Create')
)
)
Here is my model:
class Catalog(models.Model):
products = models.CharField(max_length=200)
def __unicode__(self):
return self.products
class Issue(models.Model):
catalog = models.ForeignKey(Catalog, related_name='issue_products')
Volume = models.DecimalField(max_digits=3, decimal_places=1)
def __unicode__(self):
return unicode(self.Volume)
class Annual(models.Model):
catalog = models.ForeignKey(Catalog, related_name='annual_products')
year_id = models.IntegerField(max_length=4)
start_date = models.CharField(max_length=6)
end_date = models.CharField(max_length=6)
def __unicode__(self):
return unicode(self.year_id)
#def __unicode__(self):
# return unicode(self.id)
class Annual_Issue(models.Model):
annual_id = models.ForeignKey(Annual, related_name='annual_ids')
issue_id = models.ForeignKey(Issue, related_name='issues')
def __unicode__(self):
return self.annual_id
class Article(models.Model):
catalog = models.ForeignKey(Catalog, related_name='article_products')
title = models.CharField(max_length=200)
abstract = models.TextField(max_length=1000, blank=True)
full_text = models.TextField(blank=True)
proquest_link = models.CharField(max_length=200, blank=True, null=True)
ebsco_link = models.CharField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.title
class Order(models.Model):
user = models.ForeignKey(User, related_name='who_ordered')
annuals = models.ForeignKey(Catalog, related_name='annuals_ordered', blank=True, null=True)
issues = models.ForeignKey(Catalog, related_name='issues_ordered', blank=True, null=True)
articles = models.ForeignKey(Catalog, related_name='items_ordered', blank=True, null=True)

In your Order model, you have defined a ForeignKey relationship for several other models (Annual, Issue, and Article), but each of these relationships points to the Catalog model. When you attempt to save the Order instance created by your form, it has received objects of these types (Annual, Issue, and Article), but it cannot store a foreign-key reference to these objects in the fields defined on the Order model. This is due to the foreign-key fields on the Order demanding that they can only contain a reference to Catalog objects.
If, for each of these foreign-key relationships, you wish to store one of these various kinds of objects, you will need to alter your Order model definition to expect references to objects of those models rather than Catalog objects.
In brief, I would suggest that the Order model be modified to include the following relationships. This will allow an order object to store a single reference to an object of each other kind (Annual, Issue, and Article).
annuals = models.ForeignKey(Annual, related_name='annuals_ordered', blank=True, null=True)
issues = models.ForeignKey(Issue, related_name='issues_ordered', blank=True, null=True)
articles = models.ForeignKey(Article, related_name='items_ordered', blank=True, null=True)
For more information about ForeignKey relationships in Django, see the reference here.

Related

Autofilling Django model form field with data from associated objects

I have a model form that creates a new job entry, and on submission, I need an invisible field job_time_estimation to be set to a sum of 'service_stats_estimate_duration' values from ServiceItemStats objects associated with the JobEntry by a many-to-many relationship when submitting the form.
For example, if in my NewJobEntryForm I chose two existing ServiceItemStats objects that have service_stats_estimate_duration values 60 and 90, on submission, I want a value 150 to be saved in that JobEntry object's job_time_estimation attribute.
I tried doing this using aggregation by defining a save() method in the model but I am getting an error "name 'serviceItemStats' is not defined".
I am not sure if I am going about this the right way. Any help would be appreciated.
My code:
models.py:
class ServiceItemStats(models.Model):
service_stats_name = models.CharField(primary_key=True, max_length=20)
service_stats_estimate_duration = models.IntegerField()
# Many-to-many relationship with JobEntry.
def __str__(self):
return self.service_stats_name
class JobEntry(models.Model):
# PK: id - automatically assigned by Django.
jo
b_entry_date_time = models.DateTimeField(default=timezone.now)
jo
b_date = models.DateField(blank=True, null=True)
job_checked_in = models.BooleanField()
job_checked_out = models.BooleanField(default=False)
job_priority = models.IntegerField()
job_time_estimation = models.IntegerField(blank=True, null=True)
job_comments = models.TextField(max_length=200, blank=True, null=True)
job_parts_instock = models.BooleanField(default=False)
job_started = models.BooleanField(default=False)
job_finished = models.BooleanField(default=False)
job_expand_fault_evidence = models.ImageField(blank=True, null=True)
job_expand_comments = models.ImageField(blank=True, null=True)
job_expand_parts_required = models.CharField(max_length=200, blank=True, null=True)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE) #One-to-one relationship
customer = models.ForeignKey(Customer, on_delete=models.CASCADE) #One-to-one relationship
serviceBay = models.ForeignKey(ServiceBay, on_delete=models.CASCADE, blank=True, null=True) #One-to-one relationship
serviceItemStats = models.ManyToManyField(ServiceItemStats, blank=True) #Many-to-many relationship
def __str__(self):
return self.id
def save(self, *args, **kwargs):
if not self.job_time_estimation:
self.job_time_estimation = serviceItemStats.objects.all().aggregate('service_stats_estimate_duration')
return super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("jobs:job_detail",kwargs={'pk':self.pk})
views.py
class JobCreateView(FormView):
template_name = "jobs/jobentry_form.html"
form_class = NewJobEntryForm
success_url = reverse_lazy("jobs:job_list")
def form_valid(self, form):
form.save()
return super(job_list, self).form_valid(form)
forms.py
class NewJobEntryForm(ModelForm):
class Meta:
model = JobEntry
fields = ['vehicle', 'customer', 'job_date', 'job_checked_in', 'job_priority', 'job_comments', 'job_parts_instock', 'serviceItemStats']
widgets = {
'job_date' : forms.DateInput(format=('%m/%d/%Y'), attrs={'class':'form-control', 'placeholder':'Select a date', 'type':'date'}),
'ServiceItemStats' : forms.CheckboxSelectMultiple(),
'job_priority' : forms.RadioSelect(choices=priorityOptions),
}
You can try this.
from django.db.models import Sum
class JobCreateView(FormView):
template_name = "jobs/jobentry_form.html"
form_class = NewJobEntryForm
success_url = reverse_lazy("jobs:job_list")
def form_valid(self, form):
job=form.save()
estimation = job.serviceItemStats.all().aggregate(total=Sum('service_stats_estimate_duration'))
job.job_time_estimation = estimation['total']
job.save()
return super(job_list, self).form_valid(form)

Custom "business" logic using pure Django

I am set on building a small website using django. What i am trying to do right now is using a CreateView based on a Model "Order", which one of its fields is another model "Customer".
The form itself works to create Orders, but im trying to find out how i can validate that the Customer that was selected is "enabled" (there is a status in the Customer model).
I was trying using the clean method but it doesnt even seem to be executing. I tried just raising the error on the clean, without validating anything, and still doesnt work. Any clue what might be wrong?
My Form:
class OrderForm(ModelForm):
def clean_customer(self, *args, **kwargs):
raise forms.ValidationError("This customer is banned.")
class Meta:
model = Order
fields = '__all__'
Relevant Models:
class Order(models.Model):
ORDER_STATUS = (('Pending Delivery', 'Pending Delivery'),
('Book on Customer', 'Book on Customer'),
('Overdue', 'Overdue'),
('Completed','Completed'))
customer = models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL)
book = models.ForeignKey(Book, null=True, on_delete=models.SET_NULL)
date_created = models.DateTimeField(auto_now_add=True)
date_due = models.DateTimeField(null=True)
date_completed = models.DateTimeField(null=True, blank=True)
status = models.CharField(max_length=100, choices=ORDER_STATUS, null=True)
def get_absolute_url(self):
return reverse('orders')
class Customer(models.Model):
status = models.CharField(max_length=10, null = True, choices=STATUS_CHOICES)
name = models.CharField(max_length=200, null=True)
username = models.CharField(max_length=25, null = True)
email = models.EmailField(null=True)
def __str__(self):
return str(self.name)+" - " +str(self.username)
def get_absolute_url(self):
return reverse('customers')
The View:
class OrderCreateView(CreateView):
model = Order
fields = '__all__'
def form_valid(self, form):
return super().form_valid(form)

ListView unrelated model lookup

I'm looking for a way to bring data from one model into a view of another model for a ListView. Basically, when the user opens up the listview of the vocabulary model, I need to look up and pull out the associated pinyin field from the dictionary model by using the vocab field. There is some example code at the bottom of the codeview that would be how I would match up a Vocabulary.vocab with a Dictionary.pinyin.
############### Models
​
class Vocabulary(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
vocab = models.CharField(max_length=255, blank=False, null=False)
translation = models.CharField(max_length=255, blank=False, null=False)
level = models.PositiveSmallIntegerField(blank=True, null=True, choices=LEVELS, default=0)
audio = models.URLField(blank=True, null=True)
objects = RandomManager()
​
class Meta:
constraints = [
models.UniqueConstraint(fields=['created_by', 'vocab'], name='User Vocab Duplicate Check')
]
verbose_name = "Chinese Vocabulary"
verbose_name_plural = verbose_name
​
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.vocab
​
class Dictionary(models.Model):
traditional = models.CharField(max_length=20)
simplified = models.CharField(max_length=20)
pinyin = models.CharField(max_length=255)
simplified_radical = models.CharField(max_length=20)
hsk_level = models.PositiveSmallIntegerField(blank=True, null=True, choices=LEVELS, default=0)
frequency_rank = models.PositiveIntegerField(blank=True, null=True)
phrase_url = models.CharField(max_length=200)
radical_url = models.CharField(max_length=200)
definition = models.TextField()
objects = RandomManager()
​
class Meta:
verbose_name = "Dictionary"
verbose_name_plural = "Dictionary"
​
def __str__(self):
return self.simplified
​
################# Views
​
class VocabDetailView(LoginRequiredMixin, DetailView):
model = Vocabulary
template_name = 'library/vocab_details.html'
​
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['dictionary'] = Dictionary.objects.get(simplified=self.object.vocab)
return context
​
​
class VocabListView(LoginRequiredMixin, ListView):
model = Vocabulary
template_name = 'library/vocab_list.html'
paginate_by = 50
def get_queryset(self):
return Vocabulary.objects.filter(created_by=self.request.user)
​
'''
hao = Vocabulary.objects.get(vocab='你')
haodict = Dictionary.objects.get(simplified=hao.vocab)
haopin = hao_dict.pinyin
'''
If you objects were related, you could override the get_queryset to use select_related or similar in order to get the related objects. But it doesn't look like you can do that because the objects aren't related.
So you could either not use a ListView and gather your objects in the context, or override the context to get the Dictionary objects you want. Sounds like you'd do something like;
def get_context_data(**kwargs):
context = super().get_context_data(**kwargs)
dicts = Dictionary.objects.filter(pinyin__in=self.object_list.values_list('vocab', flat=True))
context['dictionaries'] = dicts
return context

How to create an inline formset for a reverse foreign key relationship

I have a Property Model as follows =
class Property(models.Model):
property_type = models.CharField(max_length=255, default='Apartment')
specifications = models.CharField(max_length=255, default='Basic')
built_up_area = models.FloatField(max_length=6, null=False, default=0)
total_area = models.FloatField(null=False, default=0)
number_of_bedrooms = models.CharField(max_length=3, default=1)
number_of_bathrooms = models.CharField(max_length=3, default=1)
number_of_parking_spaces = models.CharField(max_length=2, default=0)
address_line_one = models.CharField(max_length=255, null=False)
address_line_two = models.CharField(max_length=255, null=True, default=None)
connectivity = models.CharField(max_length=255, default=None, null=True)
neighborhood_quality = models.CharField(max_length=255, default=None,
null=True)
comments = models.CharField(max_length=255, default=None, null=True)
city = models.ForeignKey('City')
state = models.ForeignKey('State')
pin_code = models.ForeignKey('PinCode')
developer = models.ForeignKey('Developer', null=True, default=None)
owner = models.ForeignKey('Owner', null=True, default=None)
created_by = models.ForeignKey('custom_user.User')
project = models.ForeignKey('Project')
def __unicode__(self):
return self.property_type
class Meta:
verbose_name_plural = 'Properties'
And a City model as follows -
class City(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(City, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
Now I want to make a single form where I can enter the Property details and while entering the city, I can enter the name of the city instead of selecting from the dropdown list.
So how do I create the inline formset using the inlineformset_factory to create the form?
==EDIT==
I've tried to use the following code to create the formset
CityFormset = inlineformset_factory(City, Property,
fields=('city',),
extra=0,
min_num=1,
can_delete=False)
You've misunderstood what an inline formset is. It's for editing the "many" side of a one-to-many relationship: that is, given a parent model of City, you could edit inline the various Properties that belong to that city.
You don't want a formset at all to simply edit the single City that a property can belong to. Instead, override the city field within your Property form to be a TextField, and either create a new City or find an existing one in the clean_city method.
class PropertyForm(forms.ModelForm):
city = forms.TextField(required=True)
class Meta:
model = Property
exclude = ('city',)
def __init__(self, *args, **kwargs):
super(PropertyForm, self).__init__(*args, **kwargs)
if self.instance and not self.data:
self.initial['city'] = self.instance.city.name
def save(self, commit=True):
city_name = self.cleaned_data['city']
city, _ = City.objects.get_or_create(name=city_name)
instance = self.save(commit=False)
instance.city = city
if commit = True:
instance.save()
return instance

Django forms target foreign key

I have a form for submitting an order. Multiple items have been attached to a catalog object, I'd like to have the form dropdown contain options for all of the items attached to the foreign key, instead of the foreign key Catalog name of Available. I know how to access these in the view, using the related name, is this possible in forms?
Here is my current form:
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, ButtonHolder, Submit
from . import models
class OrderListForm(forms.ModelForm):
class Meta:
fields = ('order_lines',)
model = models.Order
def __init__(self, *args, **kwargs):
super(OrderListForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'order_lines',
ButtonHolder(
Submit('create', 'Create')
)
)
Here is my model:
class Catalog(models.Model):
products = models.CharField(max_length=200)
def __unicode__(self):
return self.products
class Issue(models.Model):
catalog = models.ForeignKey(Catalog, related_name='issue_products')
Volume = models.DecimalField(max_digits=3, decimal_places=1)
def __unicode__(self):
return unicode(self.catalog)
class Annual(models.Model):
catalog = models.ForeignKey(Catalog, related_name='annual_products')
year_id = models.IntegerField(max_length=4)
start_date = models.CharField(max_length=6)
end_date = models.CharField(max_length=6)
def __unicode__(self):
return unicode(self.year_id)
class Annual_Issue(models.Model):
annual_id = models.ForeignKey(Annual, related_name='annual_ids')
issue_id = models.ForeignKey(Issue, related_name='issues')
def __unicode__(self):
return self.annual_id
class Article(models.Model):
catalog = models.ForeignKey(Catalog, related_name='article_products')
title = models.CharField(max_length=200)
abstract = models.TextField(max_length=1000, blank=True)
full_text = models.TextField(blank=True)
proquest_link = models.CharField(max_length=200, blank=True, null=True)
ebsco_link = models.CharField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.title
class Order(models.Model):
user = models.ForeignKey(User, related_name='who_ordered')
order_lines = models.ForeignKey(Issue, related_name='items_ordered')
you can access all the Annuals and Articles that are in the same catalog by using:
c = Catalog.objects.get(....
c.article_products_set.all()
c.annual_products_set.all()