Back Referencing Django models - django

I've 3 models.
class ShipmentWeightMapping(models.Model):
id = models.IntegerField(primary_key = True)
weight = models.CharField(max_length = 255)
status = models.CharField(max_length = 255)
time = models.DateTimeField(auto_now = True, auto_now_add = True)
shipment_id = models.ForeignKey('Shipment')
class ShipmentDimensionMapping(models.Model):
id = models.IntegerField(primary_key = True)
status = models.CharField(max_length = 255)
time = models.DateTimeField(auto_now = True, auto_now_add = True)
length = models.IntegerField()
breadth = models.IntegerField()
height = models.IntegerField()
shipment_id = models.ForeignKey('Shipment')
class Shipment(models.Model):
id = models.IntegerField(primary_key = True)
job_id = models.CharField(max_length = 255)
weights = models.ForeignKey('ShipmentWeightMapping')#backref
dimensions = models.ForeignKey('ShipmentDimensionMapping')#backref
time = models.DateTimeField(auto_now = True, auto_now_add = True, db_index = True)
I want to backref weights and dimensions to their respective classes, so that when I query Shipment model for id=1, I should get length, breadth and height from ShipmentDimensionMapping and weight from ShipmentWeightMapping without querying ShipmentDimensionMapping and ShipmentWeightMapping separately.
For eg:- Currently I do like this.
To get Shipment Details for id = 1, I do the following.
dimension_obj = ShipmentDimensionMapping.objects.filter(shipment_id = 1)[0]
length = dimension_obj.length
#similarly for other details in ShipmentDimensionMapping
weight_obj = ShipmentWeightMapping.objects.filter(shipment_id = 1)[0]
weight = weight_obj.weight
#similarly for other details in ShipmentWeightMapping
shipment_obj = Shipment.objects.filter(shipment_id = 1)[0]
job_id = shipment_obj.job_id
#similarly for other details in Shipment
Is there any way in which I only query shipment_obj and I get details of ShipmentWeightMapping and ShipmentDimensionMapping?
Also, I always use the result at zeroth index [0] to get the result. Although the result returned always contains only 1 item, still I need to do [0]. How can I avoid this as well?

Since weight and dimension data are on other tables, you can't get them without querying these two tables.
For the second question, if you know there's only one entry, you can do:
shipment_obj = Shipment.objects.get(id=1)
BTW; you don't have to add ID values explicitly since they are called id in your code as well which is by default in Django.

Related

Django Testing IntegrityError: duplicate key value violates unique constraint DETAIL: Key (project_id)=(1023044) already exists

I have not been able to resolve this IntegrityError issue in my Django's unittest. Here are my models:
class UserProfile(models.Model):
''' UserProfile to separate authentication and profile '''
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete = models.CASCADE, null = True, blank = True)
# Note: first name and last name is in the custom User model
first_name = models.CharField(max_length = 20, blank = True, null = True)
last_name = models.CharField(max_length = 30, blank = True, null = True)
address = models.CharField(max_length = 100, null=True, blank = True)
address_city = models.CharField(max_length = 30, null = True, blank = True)
metropolitan = models.CharField(max_length = 30, null = True, blank = False)
class Municipality(models.Model):
name = models.CharField(max_length = 50)
abb = models.CharField(max_length = 5)
date_created = models.DateTimeField(auto_now_add = True)
date_modified = models.DateTimeField(auto_now = True)
userprofile = models.ForeignKey('user_profile.UserProfile', blank = False, null = False, related_name = 'userprofile_municipalities', on_delete = models.CASCADE)
class Project(models.Model):
name = models.CharField(max_length = 50)
logo = models.ImageField(null=True, blank = True, width_field = 'logo_width', height_field = 'logo_height')
logo_height = models.IntegerField(default = 40)
logo_width = models.IntegerField(default = 40)
date_created = models.DateTimeField(auto_now_add = True )
date_modified = models.DateTimeField(auto_now = True )
# RELATIONSHIPS:
user_profiles = models.ManyToManyField('user_profile.UserProfile', through = 'ProjectAssociation', through_fields = ('project', 'user_profile' ), blank = True, related_name = 'user_projects')
address = models.OneToOneField(Address, on_delete = models.PROTECT, null = True, blank = True)
municipality = models.ForeignKey('development.Municipality', related_name = 'municipality_projects', null = False, blank = False)
class Job(models.Model):
project = models.OneToOneField('project_profile.Project', blank = False, on_delete = models.CASCADE, related_name = 'job')
...
date_modified = models.DateTimeField(auto_now_add = True)
date_created = models.DateTimeField(auto_now = True)
class Invoice(models.Model):
PO = models.CharField(max_length = 50, blank = False, null = False) # e.g. Mixed Use Residential Commercial Rental Invoice
invoice_type = models.CharField(max_length = 40)
date_created = models.DateTimeField(auto_now = True)
date_modified = models.DateTimeField(auto_now_add = True)
job = models.ForeignKey(DevelopmentProject, related_name = 'job_invoices', blank = True, null = True, on_delete = models.CASCADE)
Invoice_creator = models.ForeignKey('user_profile.UserProfile', related_name = 'created_invoices', blank = True, null = True, on_delete = models.SET_NULL) # ModelForm to enforce If the Invoice creator's account is closed, the corresponding Invoices to be preserved
Invoice_reviewer = models.ForeignKey('user_profile.UserProfile', related_name = 'reviewed_invoices', blank = True, null = True , on_delete = models.SET_NULL ) # The reviewer is not necessary, but
...
In my unittest, I am getting integrity error message even when I try to explicitly assign unique id to the created instance:
class UpdateinvoiceTestCase(TestCase):
''' Unit Test for Updateinvoice View '''
def setUp(self):
self.factory = RequestFactory()
# Create the dependencies
self.userprofile = mommy.make('user_profile.UserProfile')
print ('User profile: ', self.userprofile, ' - userprofile id: ', self.userprofile.id )
self.municipality = mommy.make('development.municipality', userprofile = self.userprofile, _quantity=1)
self.project = mommy.make('project_profile.Project', municipality = self.municipality[0], _quantity=2)
self.job = mommy.make('development.Job', project = self.project[0] )
# Create invoice
self.invoice = mommy.make('development.invoice', job = self.job)
# Passing the pk to create the url
the_uri = reverse('development:update_invoice', args=(self.invoice.pk,))
the_url = 'http://localhost:8000' + reverse('development:update_invoice', args=(self.invoice.pk,))
# Creating a client:
self.response = self.client.get(the_url, follow=True)
def test_url(self):
''' Ensure that the url works '''
self.assertEqual(self.response.status_code, 200)
I have made sure only one test is run using so there is no sharing of the data between different testcases that would throw Django off:
python manage.py test project.tests.test_views.UpdateViewTestCase
I get the the following error message:
IntegrityError: duplicate key value violates unique constraint "development_developmentproject_project_id_key"
DETAIL: Key (project_id)=(1) already exists
I also tried using mommy.make to create project, but I got the same error message. I also tried to specifically assign non-existent ids to the Project creation line, but could not convince Django to stop complaining.
So, Project is being created twice, but I cannot figure out why and where. Any help is much appreciated!
It turned out that I've used signals which created an instance already and I was creating the same instance in my setUpTestData again. The solution was to avoid creating a duplicate instance or simply use get_or_create instead of create or mommy.make

limit_choices_to within Django model

I have a project model. This project contains persons (those who are working on the project). I am trying to also make a model for each project person, including any notes they have on the project and % complete on project.
My issue is that I want to filter the individual_person_in_project to only the persons within the corresponding project. I am trying to use
limit_choices_to = {'person_in_project':User}
I want to limit my choices to users who are persons in my Project model.
class Project(models.Model):
project_name = models.CharField(max_length = 120,null = False,blank = False)
project_percent_complete = models.IntegerField(blank = True,null = True, default = 0)
person_in_project = models.ManyToManyField(User,related_name = 'project_person',blank = True)
project_description = models.CharField(max_length = 300,null = True,blank = True)
class Project_Person(models.Model):
corresponding_project = models.ForeignKey(Project,related_name = 'corresponding_project_this_user_is_in',null = False)
individual_person_in_project = models.ForeignKey(User, related_name = 'a_person_within_the_corresponding_project', limit_choices_to = {'person_in_project':User})
percent_complete = models.IntegerField(default = 0)
I left a comment above, but I think this is a better answer, anyhow:
You can use the through option to track extra information on the manytomanyfield, so you get:
class Project(models.Model):
...
person_in_project = models.ManyToManyField(User, related_name='project_person', blank=True, through=ProjectPerson)
The docs explain the rest of the details, but you shouldn't have to handle the limit_choices_to in that case.
Thank you for your help, it was very useful . The most helpful comment was ryanmrubin and the use of through with ManyToManyField to facilitate their relationship I ended up creating a separate class and associating that with a project.
If I need to tie more information into this new class I will certainly use through with the ManyToManyField.
class Project(models.Model):
project_name = models.CharField(max_length = 120,null = False,blank = False)
project_percent_complete = models.IntegerField(blank = True,null = True, default = 0)
project_description = models.CharField(max_length = 300,null = True,blank = True)
people_in_project = models.ManyToManyField(User,blank = True)
class Project_Tasks(models.Model):
description = models.CharField(max_length = 120,blank = True)
percent_complete = models.IntegerField(default = 0)
user = models.OneToOneField(User,unique = True,blank = True, null = True)
project = models.OneToOneField(Project,unique = True, blank = False, null = True)

DISTINCT Django Query

I want to use distinct() in Django but want to return the QuerySet not ValueSet.
Since I want to distinct on a particular column and get all the other columns as well, I cannot use a ValueSet.
Currently I'm using:-
daily_count = ShipmentSubSortScanMapping.objects.all().values('shipment_id').distinct()
This return only shipment_id, but I want all the fields.
Here's my model:-
class ShipmentSubSortScanMapping(models.Model):
received_arm_id = models.CharField(max_length = 255)
actual_arm_id = models.CharField(max_length = 255, default = 'None')
shipment_id = models.ForeignKey('Shipment',related_name ='subsortscans')
time = models.DateTimeField( auto_now_add = True)
distinct accepts an argument of what fields to operate on, so probably you want:
daily_count = ShipmentSubSortScanMapping.objects.all().distinct('shipment_id')

Django ORM JOIN query

I've 2 models
class ShipmentBagSealMapping(models.Model):
bag_seal = models.CharField(max_length = 255)
status = models.CharField(max_length = 255, default = 'open')
time = models.DateTimeField( auto_now_add = True, db_index = True)
shipment_id = models.ForeignKey('Shipment', related_name = 'bags')
class Shipment(models.Model):
job_id = models.CharField(max_length = 255)
time = models.DateTimeField( auto_now_add = True, db_index = True)
I want to write a JOIN query which tells me the count of records in ShipmentBagSealMapping with status = close and time of Shipment is in range [start_time and end_time].
Here' what I tried:
total_bags = ShipmentBagSealMapping.objects.filter(shipments__time__range = [start_time,end_time],status='close').values('bag_seal').distinct().count()
But it throws an error saying :-
Cannot resolve keyword 'shipments' into field. Choices are: bag_seal, id, shipment_id, status, time
How do I do it?
This should do it:
total_bags = ShipmentBagSealMapping.objects.filter(shipment_id__time__range = [start_time,end_time],status='close').values('bag_seal').distinct().count()
See you have defined the field as shipment_id not shipments
Django will automatically add _id in your ForeignKey, so make some change like this may help
class ShipmentBagSealMapping(model.Model):
bag_seal = models.CharField(max_length = 255)
status = models.CharField(max_length = 255 )
time = models.DateTimeField( auto_now_add = True)
shipments = models.ForeignKey('Shipment', related_name = 'bags')

Django form from related model

You have models:
class Order(Model):
date = DateField(editable = False, auto_now_add=True)
status = CharField(max_length = 1, choices = STATUS, default = 'N')
profile = ForeignKey(Profile, related_name = 'orders', blank = True, null = True)
shipping = ForeignKey(Shipping, related_name = 'orders', blank = True, null = True)
address = ForeignKey(Address, related_name = 'address_orders', blank = True, null = True)
company = ForeignKey(Company, related_name = 'company_orders', blank = True, null = True)
class Address(Model):
address_profile = ForeignKey(Profile, related_name = 'addresses')
city = CharField(max_length = 256, blank = True, null = True)
street = CharField(max_length = 256, blank = True, null = True)
zipcode = CharField(max_length = 10, blank = True, null = True)
phone = CharField(max_length = 23, blank = True, null = True)
class Company(Address):
company_profile = ForeignKey(Profile, related_name = 'companies')
name = CharField(max_length = 256, blank = True, null = True)
company_id = CharField(max_length = 256, blank = True, null = True)
How do you create OrderForm for specified profile? With this one
class OrderCheckoutForm(forms.ModelForm):
class Meta:
model = Order
I get a form with all addresses and companies in option. I'd like to limit them to related with it's profile. Is there any simple solution?
Thanks in advance,
Etam.
You can feed a queryset to your ModelChoiceField (which is what ModelForms use for ForeignKeys; you can find it in django/forms/models.py). Something like,
class OrderCheckoutForm(forms.ModelForm):
profile = models.ModelChoiceField(queryset = Profile.objects.filter(...))
class Meta:
model = Order
Of course, your filtering criterion will depend on what you are trying to accomplish, which isn't 100% clear from your original post.