Either/Or fields in Django models - django

I was building a django model that has two fields, only one of them is required.
class course_data(models.Model):
link = models.CharField(max_length = 500, null = True, blank = True)
uploaded_file = models.FileField(upload_to='course_files', null = True, blank = True)
name = models.CharField(max_length = 200, null = True, blank = True)
description = models.TextField(null = True, blank = True)
applies_to = models.ManyToManyField('course')
I want the entry to be valid only if the "link" field is provided or if the 'uploaded_file' field is provided or both. I can't make both parameters optional since they can be both left blank. Any suggestions?

This isn't something you'd deal with in the model definition. You would handle it at the form level, by making both fields required=False but checking in the form's clean() method that one of them was supplied.

you can override clean method:
class course_data(models.Model):
link = models.CharField(max_length = 500, null = True, blank = True)
uploaded_file = models.FileField(upload_to='course_files', null = True, blank = True)
name = models.CharField(max_length = 200, null = True, blank = True)
description = models.TextField(null = True, blank = True)
applies_to = models.ManyToManyField('course')
def clean(self):
if self.link == None and self.uploaded_file is None:
raise ValidationError(_('Fields uploaded_file and link required.'))

You can use a ModelForm and override its clean method to get the behaviour you want. If you want to use the admin, you can add the custom behaviour with a ModelAdmin

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

Set the primary_key based on two other fields in Django

I have the following model in which one the fields is based on other fields (similar to this):
from django.db import models
class Model_Model01(models.Model):
code_01 = models.CharField(max_length = 2, null = False, blank = False)
code_02 = models.CharField(max_length = 3, null = False, blank = False)
code_combined = models.CharField(max_length = 6, null = True, blank = False, primary_key = False)
def save(self, *args, **kwargs):
self.code_combined = "{}.{}".format(self.code_01, self.code_02)
super(Model_Model01, self).save(*args, **kwargs)
def __unicode__(self):
return self.code_combined
I would like to set the primary_key of code_combined field as True after overriding the save method so I can use it as a foreignkey on other model. Is there a way to do this?
Why not tell Django that you are using a composite key for this model using unique_together?
Composite primary key in django
https://docs.djangoproject.com/en/2.0/ref/models/options/#unique-together
class Model_Model01(models.Model):
code_01 = models.CharField(max_length = 2, null = False, blank = False)
code_02 = models.CharField(max_length = 3, null = False, blank = False)
class Meta:
unique_together = ('code_01', 'code_02',)
Update
Why not have the code_combined field be Unique, and reference it in your ForeignKey as the to_field?
class Model_Model01(models.Model):
code_01 = models.CharField(max_length = 2, null = False, blank = False)
code_02 = models.CharField(max_length = 3, null = False, blank = False)
code_combined = models.CharField(max_length = 6, unique = True)
def save(self, *args, **kwargs):
self.code_combined = "{}.{}".format(self.code_01, self.code_02)
super(Model_Model01, self).save(*args, **kwargs)
def __unicode__(self):
return self.code_combined
class Model_Model02(models.Model):
model_1 = models.ForeignKey(Model_Model01, to_field='code_combined')
...
Beware of Unicode here; unless you are using Python 3!

Django Related Field based filter

I have two models based in which one is related to another.
class Contacts(BaseModel):
user = models.ForeignKey(User, related_name='contacts')
group = models.ForeignKey(ContactGroups, related_name = 'contact_list')
name = models.CharField(_("Name"), max_length = 250, null = True, blank = True)
title = models.CharField(_("Title"), max_length = 250, null = True, blank = True)
twitter = models.CharField(_("Twitter"), max_length = 250, null = True, blank = True)
facebook = models.CharField(_("Facebook"), max_length = 250, null = True, blank = True)
google = models.CharField(_("Google +"), max_length=250, null = True, blank = True)
notes = models.TextField(_("Notes"), null = True, blank = True)
image = ImageField(_('mugshot'), upload_to = upload_to, blank = True, null = True)
class PhoneNumbers(BaseModel):
contact = models.ForeignKey(Contacts, related_name='phone_numbers')
phone = models.CharField(_("Phone"), max_length = 200, null = True, blank = True)
type = models.CharField(_("Type"), max_length = 200, choices = TYPES, null = True, blank = True)
Here I wanted filter contacts who have atleast one phonenumber.
I have tried,
contacts = Contacts.objects.filter(user=request.user, phone_numbers__isnull=True)
Is it correct or not? Is there any other optimized method? How can we filter queryset based on related names?
I think you want,
contacts = Contacts.objects.filter(user=request.user, phone_numbers__isnull=False)
i.e check for False rather than True, as you want Contacts where at least one PhoneNumber is there. If its not there it will be NULL.

How to restrict models foreign keys to foreign objects having the same property

Here is my example :
We have printers. We can define page formats that are linked to a specific printer then we define workflows that select a starting format (first page added to the printing job), a body format and an end format (last page added to the printing job).
Start and End are not required (null and blank = True)
I want to be sure that start, body and end will be formats of the same printer.
class Printer(models.Model):
name = models.CharField(max_length = 20)
class Format(models.Model):
name = models.CharField(max_length = 20)
format = models.TextField()
printer = models.ForeignKey(Printer)
class Workflow(models.Model):
name = models.CharField(max_length = 20)
startfmt = models.ForeignKey(Format, related_name = 'start_workflow', null = True, blank = True)
bodyfmt = models.ForeignKey(Format, related_name = 'start_workflow')
endfmt = models.ForeignKey(Format, related_name = 'start_workflow', null = True, blank = True)
So I need this model to validate that startfmt, bodyfmt and endfmt reference formats that share the same printer... how ?
Your best bet is probably overriding save in the Workflow model:
class Workflow(models.Model):
# field definitions as you have them
def save(self, force_insert=False, force_update=False):
printer = self.bodyfmt.printer
if self.startfmt and self.startfmt.printer != printer:
raise ValueError("Startfmt printer does not match")
if self.endfmt and self.endfmt.printer != printer:
raise ValueError("Endfmt printer does not match")
super(Workflow, self).save(force_insert, force_update)

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.