I need to write any data to ManyToManyField via Model's form in the template, but i get an error like "... needs to have a value for field "id" before this many-to-many relationship can be used.". It shows when I try to use self.service("service" is my ManyToManyField) in my overrided save() method. I know that ManyToManyField is not basic field and it returns something like queryset, but how can i manipulate data inside save() method, because "self.service" doesn't work.
# models.py
class Appointments(models.Model):
name = models.CharField(max_length=200, db_index=True, verbose_name='Имя, фамилия')
tel = models.CharField(max_length=200, db_index=True, verbose_name='Номер телефона')
e_mail = models.CharField(max_length=200, blank=True, db_index=True, verbose_name='E-mail')
car = models.ForeignKey('Cars', null=True, on_delete=models.PROTECT, verbose_name='Тип автомобиля')
num_car = models.CharField(max_length=200, null=True, db_index=True, verbose_name='Гос.номер автомобиля')
**service = models.ManyToManyField(Services, verbose_name='Тип услуги')**
date_created = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Дата публикации заявки')
date_service = models.DateField(db_index=True, verbose_name='Дата')
time_service = models.TimeField(db_index=True, help_text="Введите время в таком формате: 15:00", verbose_name='Время')
price = models.CharField(max_length=50, db_index=True, null=True, verbose_name='Цена')
def save(self, *args, **kwargs):
for i in Services_prices.objects.all():
ccar = i.car
sservice = i.service
for d in self.service:
if self.car == ccar and d == sservice:
self.price = i.price
break
elif ccar == None and d == sservice:
self.price = i.price
break
super().save(*args, **kwargs)
# forms.py
class AppointmentForm(forms.ModelForm):
service = forms.ModelMultipleChoiceField(queryset=Services.objects.all(), required=False, widget=forms.CheckboxSelectMultiple())
class Meta:
model = Appointments
fields = ('name', 'tel', 'e_mail', 'car', 'num_car', 'service', 'date_service', 'time_service')
In order to have a many_to_many relation between two objects, you need primary keys of the both objects. Before calling super's save, your model does not have a primary key yet.
In your overriden save method, call super first, (e.g.super().save(*args, **kwargs)) then do your stuff, then save again.
Related
Hi I have the following django model:
class Issue(models.Model):
title = models.CharField(max_length=200)
date = models.DateTimeField(auto_now=True)
assignee = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assignee')
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner', null=True, blank=True)
description = models.TextField()
state = models.IntegerField(choices=STATUS_CHOICES, default=1)
priority = models.IntegerField(choices=RELEVANCE_CHOICES, default=2)
expired_date = models.DateField(auto_now=False, null=True, blank=True)
and a form which allow a user to create an Issue instance:
class IssueForm(forms.ModelForm):
class Meta:
model = Issue
fields = ('title', 'description', 'assignee', 'state', 'priority', 'expired_date')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "Titolo"
self.fields['description'].label = "Descrizione"
self.fields['state'].label = "Stato"
self.fields['priority'].label = "Priorità"
self.fields['expired_date'].label = "Termine"
self.fields['expired_date'].widget.attrs.update({'class': 'datepicker'})
self.fields['assignee'] = forms.MultipleChoiceField(
choices=self.fields['assignee'].choices,
widget=forms.CheckboxSelectMultiple,
label=("Assegnatario")
)
def clean(self):
cleaned_data = super().clean()
user_id = [i for i in cleaned_data['assignee']]
cleaned_data['assignee'] = [User.objects.get(id=i) for i in user_id]
return cleaned_data
I render this form and the field assignee is a checkbox.
I would like to be able to choose several assignee for the same issue, but I got an error because the Issue model expect just one User instance
How can I modify my model Issue in order to get more than one user ?
Thanks
you can create a new class and name it Issue_Instance where every Issue Object can have an assignee as a foreign key the problem that the relation is one to many because you have to choose more than one assignee and Django doesn't support the idea of having Array or List of Foreign Keys(I don't know any frame works that do :=) ) so I would suggest creating a new class or make the foreign key relation one-to-many key field read about it it will be very useful to solve your problem
I am trying to save a form which have ForeignKey (purchaseContractID).Here is my contract Model
class contracts(models.Model):
productDetailID=models.ForeignKey('Inventory.productDetails',related_name='+',on_delete=models.CASCADE,verbose_name='Select Product',default=None)
supplierID=models.ForeignKey(suppliers,on_delete=models.CASCADE,verbose_name='Select Supplier',default=None)
totalUnits=models.IntegerField(verbose_name='Total Units',editable=False,default=None)
ratePerUnit=models.IntegerField(verbose_name='Rate Per Unit',default=None)
saleTax=models.IntegerField(verbose_name='Sale Tax',default=None)
incomeTax=models.IntegerField(verbose_name='Income Tax',default=None)
saleTaxwithHeld=models.IntegerField(verbose_name='Sale Tax with Held',default=None)
startDate=models.DateField(verbose_name='Start Date',default=None)
endDate=models.DateField(verbose_name='End Date',default=None)
manulContractNumber=models.IntegerField(verbose_name='Manul Contract Number',default=None)
paymentDays=models.IntegerField(verbose_name='Payment Days',default=None)
remarks=models.CharField(verbose_name='Remarks',max_length=100,default=None)
dateOfEntry=models.DateField(editable=False,default=datetime.now())
def __str__(self):
return str(self.productDetailID.name)
here is my inventoryIn Model which foreignKey of PurchaseContract
class inventoryIn(models.Model):
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)
purchaseContractID=models.ForeignKey('Purchase.contracts',on_delete=models.CASCADE,verbose_name='Contract ID')
unitsIn=models.IntegerField(verbose_name='Enter No of Bags')
MYCHOCIES = (('orginal', 'ORGINAL'), ('dummy', 'DUMMY'))
doType = models.CharField(blank=True, choices=MYCHOCIES, verbose_name='Select DO Type', max_length=20)
doID=models.IntegerField(verbose_name='Do No')
doImage=models.ImageField(upload_to='doImage/%Y/%m/%d',verbose_name='Do Image')
invoiceID=models.IntegerField(verbose_name='Invoice No')
invoiceImage=models.ImageField(upload_to='inventoryIn/%Y/%m/%d')
agingDate=models.DateField(verbose_name='Receiving Date')
labReportImage = models.ImageField(upload_to='labReportImage/%Y/%m/%d', blank=True,verbose_name='Lab Report Image')
enterPaymentDays = models.IntegerField(verbose_name='Enter Payment Days', blank=True, default=None)
dateOfEntry=models.DateField(default=datetime.now())
def __str__(self):
return self.supplierID
here is my admin.py where i am adding admin form and also adding a js that add some fields dynamical.
class inventoryInAdmin(admin.ModelAdmin):
fields = ['purchaseContractID','unitsIn','doType','doID','doImage','invoiceID','invoiceImage','agingDate','labReportImage','enterPaymentDays']
class Media:
js = ('js/addInventory.js',)
admin.site.register(inventoryIn,inventoryInAdmin)
it is not allowing me to submit form and giving me error "Select a valid choice. That choice is not one of the available choices."
I have resolved this issue by adding the save method at InventoryIn Model.
class inventoryIn(models.Model):
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)
purchaseContractID=models.ForeignKey('Purchase.contracts',on_delete=models.CASCADE,verbose_name='Contract ID')
unitsIn=models.IntegerField(verbose_name='Enter No of Bags')
MYCHOCIES = (('orginal', 'ORGINAL'), ('dummy', 'DUMMY'))
doType = models.CharField(blank=True, choices=MYCHOCIES, verbose_name='Select DO Type', max_length=20)
doID=models.IntegerField(verbose_name='Do No')
doImage=models.ImageField(upload_to='doImage/%Y/%m/%d',verbose_name='Do Image')
invoiceID=models.IntegerField(verbose_name='Invoice No')
invoiceImage=models.ImageField(upload_to='inventoryIn/%Y/%m/%d')
agingDate=models.DateField(verbose_name='Receiving Date')
labReportImage = models.ImageField(upload_to='labReportImage/%Y/%m/%d', blank=True,verbose_name='Lab Report Image')
enterPaymentDays = models.IntegerField(verbose_name='Enter Payment Days', blank=True, default=None)
dateOfEntry=models.DateField(default=datetime.now())
def __str__(self):
return str(self.supplierID)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
from Purchase.models import contracts,suppliers
contract = contracts.objects.values('supplierID', 'productDetailID').filter(id=self.purchaseContractID.id)
supplier=contract[0].get("supplierID")
product=contract[0].get("productDetailID")
self.supplierID=suppliers.objects.get(id=supplier)
self.productID=products.objects.get(productDetailsID=product)
super(inventoryIn,self).save()
and i also had made my two fields editable=False
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)
when there is foreign key in model, exporting is very slow, when I exclude the foreign there is no problem, it starts to download quickly. What might be the issue here? Thanks!
My code is like,
resources.py
class InvoiceResource(resources.ModelResource):
class Meta:
model = Invoices
views.py
def export_invoice(request):
person_resource = InvoiceResource()
dataset = person_resource.export()
response = HttpResponse(dataset.xlsx, content_type='application/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename="invoices.xlsx"'
return response
models.py
class Invoices(models.Model):
store_code = models.ForeignKey(MasterData, on_delete=models.CASCADE, db_column='Store Code', blank=True, null=True, verbose_name='Store Code')
erp_id = models.CharField(db_column='ERP ID', max_length=200, blank=True, null=True, verbose_name='ERP ID')
store_name = models.CharField(db_column='Store Name', max_length=200, blank=True, null=True, verbose_name='Store Name')
I think you should use select_related
class ModelA(models.Model):
fk = models.Foreingkey(ModelB)
class ModelB(models.Model):
title = models.CharField(max_lenght=20)
if you use:
a_objects = ModelA.objects.all()
for obj in a_objects:
fk = obj.fk
every object need query to database
but if you use
a_objects = ModelA.objects.all().select_related('fk')
for obj in a_objects:
fk = obj.fk
in this case only one query to database.
For more information go to https://docs.djangoproject.com/en/2.0/ref/models/querysets/
class InvoiceResource(resources.ModelResource):
def get_queryset(self):
return super().get_queryset().select_related('store_code')
class Meta:
model = Invoices
I have a bunch of message records that I would like to assign to different taskboxes.
#models.py
class TaskBox(models.Model):
name = models.CharField(max_length=64, blank=False)
def __str__(self):
return u'%s' % (self.name)
class Admin:
pass
class InboxEntry(models.Model):
job_number = models.CharField(max_length=14, unique=False, blank=False, null=False)
job_name = models.CharField(max_length=64, unique=False, blank=False, null=False)
request = models.CharField(max_length=64, choices=PRINT_CHOICES, blank=True, null=True)
date_due = models.DateTimeField(("Due"),auto_now=False)
note = models.TextField(max_length=1000, unique=False, blank=True, null=True)
assigned_by = models.ForeignKey(UserProfile, blank=False, null=False)
box = models.ForeignKey(TaskBox)
assigned_to = models.ManyToManyField(UserProfile, related_name='name', blank=True)
status = models.CharField(max_length=30, choices=STATUS_CHOICES, default="Awaiting Action")
def __str__(self):
return u'%s %s' % (self.job_number, self.job_name)
class Admin:
pass
class Meta:
ordering = ['status']
The idea is for the template to have some generic tags like {{ for taskbox in taskboxes }} to create a separate div for each taskbox that will hold a table for that box's records. My problem is constructing the view function...
#views.py
def display_prdInboxEntry(request, id):
if request.method == 'POST':
form = PrdInboxForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('taskmanager/display/'+ id +'/')
else:
form = PrdInboxForm(request.POST)
return HttpResponseRedirect('taskmanager/display/'+ id +'/')
else:
form = PrdInboxForm()
user = request.user
**taskboxes = TaskBox.objects.all()
records_1 = InboxEntry.objects.filter(taskboxes[id]=1)
records_2 = InboxEntry.objects.filter(taskboxes[id]=2)
records_3 = InboxEntry.objects.filter(taskboxes[id]=3)
..... **
return render_to_response('taskmanager/taskmanager_view.html', {'form': form, 'taskboxes': taskboxes, 'records_1' : records_1, 'records_2' : records_2, 'records_3' : records_3, 'user': user}, context_instance=RequestContext(request))
The InboxEntry model has a field called "box" that's just a reference to the TaskBox model. I need a way to map say... TaskBox id 1 with all of the InboxEntry objects with "box = 1" so that I can populate the templates appropriately. Can I construct the function to accommodate this, or am I going about it the wrong way entirely?
It sounds like you're looking for the automatically-generated attribute for reverse lookups. You can get a QuerySet of all InboxEntries associated with a TaskBox like this:
TaskBox.objects.filter(id=1).inboxentry_set.all()
See the documentation on related objects.
I need to add a conditional piece of validation to my ModelForm.
Below is my Listing Model.
LISTING_TYPES = (
('event', 'event'),
('release', 'release')
)
class Listing(models.Model):
title = models.CharField(max_length=255, verbose_name='Listing Title')
type = models.CharField(max_length=255, choices=LISTING_TYPES, verbose_name='Listing Type')
slug = models.SlugField(max_length=100)
content = models.TextField(verbose_name='Listing Overview')
competition = models.TextField()
date_start = models.DateTimeField()
time_start = models.CharField(max_length=255)
date_end = models.DateTimeField()
time_end = models.CharField(max_length=255)
pub_date = models.DateTimeField('date published', auto_now_add=True)
venue = models.ForeignKey(Venue)
class ListingForm(ModelForm):
date_start = forms.DateField(input_formats=DATE_INPUT_FORMATS)
date_end = forms.DateField(input_formats=DATE_INPUT_FORMATS)
class Meta:
model = Listing
Venue should only be required if type == 'event'. If type == 'release', I want venue to be required=False
How can I go about this?
Thanks
First Listing.venue needs to allow null values
venue = models.ForeignKey(Venue, blank=True, null=True)
Your ModelForm then needs a clean method. Something like the following
def clean(self):
cleaned_data = super(ListingForm, self).clean()
venue = cleaned_data.get("venue")
type = cleaned_data.get("type")
if type == 'event' and not venue:
raise forms.ValidationError("A venue is required for events")
You mentioned doing ModelForm validation, but you should ask yourself if this rule is specific to creating objects with forms, or whether it is inherent in your data model itself. If it's the latter, then doing model validation makes more sense.
from django.core.exceptions import ValidationError
class Listing(models.Model):
...
def clean(self):
super(Listing, self).clean()
if self.type == 'event' and not self.venue:
raise ValidationError('A venue is required for events')
This will be called during ModelForm validation, so it will have the same effect there, but defining it on the model allows you to check the consistency of your data at any point with the Model.full_clean() method.
As Iain points out, you first need to allow null values for venue.
venue = models.ForeignKey(Venue, blank=True, null=True)