Django models - field dependency - python

How can I have field dependency?
Case 1: If boolean field call_me is set, then telephone must be set, else it should be blank
Case 2: If many to many field category (with values sale, rent) has one of the values as sale, then price_sale must be set, else it should be blank

For Case 1, you can validate that easily in the model's clean method:
from django.core.exceptions import ValidationError
class MyModel(models.Model):
...
def clean(self):
if self.call_me and not self.telephone.strip():
raise ValidationError('Telephone is required')
For Case 2, M2M relationships are not added until after the model is saved, so using clean on your model won't work in this scenario. However, you can do this from the clean method of any ModelForm you use to edit this, be it in the admin or your own view.
However, having category as a M2M when the only possible values are "sale" and "rent", is poor design. Even then, "sale" and "rent" are mutually exclusive, so an M2M is inappropriate anyways (your model won't be experiencing both a "sale" and a "rent" at the same time ever).
As a result, it would be a better idea to have category be a CharField with choices consisting of "sale" and "rent". If you do it that way, you can then use your model's clean method in the same way as Case 1 for this as well.

Case 1:
Don't do it like that, have a different table for telephone numbers and have a ForeignKey from the Person (I'm assuming it's a person) to the the telephone number. If you have more than one telephone number per person, do it the other way around, otherwise consider using a OneToOne.
Obviously you'll want the ForeignKey to be nullable. That way, the only way to have a telephone number is if the person provided one.
Case 2:
I don't understand your database design here, so I can't answer. You'll have to explain more - why do you need a ManyToMany here?

Related

Django exclude from queryset if all attributes of the set not match

I have 2 models Course and Class
I'm trying to exclude the courses where ALL his classes vacancies are 0, but this query is excluding the course if only one of the class vacancies is 0.
courses = Course.objects.all().exclude(class_set__vacancies=0)
Classes:
If you are trying to get a Queryset containing a list of all the courses where there is at least one vacancy in any associated class, try this:
courses = Course.objects.filter(class__vacancies__gte = 1).distinct()
You don't need _set in your filter() argument; when the call is made to the database, its joining the Course and Class tables anyway.
As I think you know, but I'll point out for the sake of clarity, __vacancies is Django's shorthand way of representing a field in a related table; in this case, the related table is the Class table, and the related field is vacancies.
The __gte in .filter(class__vacancies__gte = 1) is "greater than or equal to", so basically this function gets a list of all the Course-Class combinations where there is at least one vacancy in the class, then returns just the Course object.
If you had a Course with two Classes, and both Classes had vacancies, Course.objects.filter(class__vacancies__gte = 1) would return duplicate Courses. I'm assuming you just want a list of the Courses with open classes, and thus duplicates are undesirable. In order to remove the duplicates, you need to include .distinct().
.distinct() can be tricky though, depending on your specific use case. See the Django docs at https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.distinct

ForeignKey vs CharField

I have an idea for data model in django and I was wondering if someone can point out pros and cons for these two setups.
Setup 1: This would be an obvious one. Using CharFields for each field of each object
class Person(models.Model):
name = models.CharField(max_length=255)
surname = models.CharField(max_length=255)
city = models.CharField(max_length=255)
Setup 2: This is the one I am thinking about. Using a ForeignKey to Objects that contain the values that current Object should have.
class Person(models.Model):
name = models.ForeignKey('Name')
surname = models.ForeignKey('Surname')
city = models.ForeignKey('City')
class Chars(models.Model):
value = models.CharField(max_length=255)
def __str__(self):
return self.value
class Meta:
abstract = True
class Name(Chars):pass
class Surname(Chars):pass
class City(Chars):pass
So in setup 1, I would create an Object with:
Person.objects.create(name='Name', surname='Surname', city='City')
and each object would have it's own data. In setup 2, I would have to do this:
_name = Name.objects.get_or_create(value='Name')[0]
_surname = Surname.objects.get_or_create(value='Surname')[0]
_city = City.objects.get_or_create(value='City')[0]
Person.objects.create(name=_name, surname=_surname, city=_city)
Question: Main purpose for this would be to reuse existing values for multiple objects, but is this something worth doing, when you take into consideration that you need multiple hits on the database to create an Object?
Choosing the correct design pattern for your application is a very wide area which is influenced by many factors that are even possibly out of scope in a Stack Overflow question. So in a sense your question could be a bit subjective and too broad.
Nevertheless, I would say that assigning a separate model (class) for first name, another separate for last name etc. is an overkill. You might essentially end up overengineering your app.
The main reasoning behind the above recommendation is that you probably do not want to treat a name as a separate entity and possibly attach additional properties to it. Unless you really would need such a feature, a name is usually a plain string that some users happen to have identical.
It doesn't make any good to keep name and surname as separate object/model/db table. In your setup, if you don't set name and surname as unique, then it doesn't make any sense to put them in separate model. Even worse, it will incur additional DB work and decrease performance. Now, if you set them as unique, then you have to work over the situation when, e.g. some user changes his name and by default it would be changed for all users with that name.
On the other hand, city - there're not that many cities and it's a good idea to keep it as separate object and refer to it via foreign key from user. This will save disk space, allow to easily get all users from same city. Even better, you can prepopulate cities DB and provide autocompletion fro users entering there city. Though for performance you might still want to keep city as a string on the user model.
Also, to mention 'gender' field, since there're not many possible choices for this data, it's worth to use enumeration in your code and store a value in DB, i.e. use choices instead of ForeignKey to a separate DB table.

What is the best way to add temporary information to a Django model? (without creating a new model)

The best way to understand my question is through code:
class ToDoList(TimeStampedModel):
DEFAULT_THEME = 1
name = models.CharField(max_length=32)
... # other fields
STATUS_CHOICES = (
('C', 'CREATED'),
('R', 'READY'),
('V', 'VALIDATED')
)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='C')
There are several types of users
Creators: who can create a ToDoList and request a validation
Validators: who can validate the list
Now, when a Validator rejects a validation request, he must also provide a reason. This will probably happen in no more than 10% of all cases, so I don't want to add a rejection_reason field on my ToDoList model.
I know one obvious solution is to create another model, called Reason, with just one char field and a FK to my ToDo model, but I would like to know if there is any better way to do this.
I don't know if this helps, but I'm using django-rest-framework for the API.
Thanks.
UPDATE
After a validator rejects the List, he must provide the reason so that the Creator can change whatever is wrong with the List. After making the changes, the Creator requests again a validation. The cycle continues until the Validator accepts the ToDoList. At this moment, the rejection reason is no longer needed, so it can be deleted.
So, in the end, the rejection reason will no longer exist and I don't think it is ok (from a db space point of view) to have a field that will only be used temporary.
You're worrying unnecessarily about storing a (presumably) small amount of text along with your ToDoList. Here's how I would handle it, if the goal was to keep it simple, and not add another model.
class ToDoList(models.Model):
name = models.CharField(...)
validated_at = models.DateTimeField(..., null=True, editable=False)
rejection_reason = models.TextFiel(..., editable=False)
Query for validated_at__isnull=False to get validated todo lists, ignoring rejection_reason altogether. Query for validated_at__isnull=True to get a list of unvalidated todo lists, and use rejection_reason to display the reason to the user. If you want to save space in your database, empty the rejection_reason field when a todo list is validated. You can also use filter (rejection_reason="") to narrow the todo lists to those that don't have a rejection reason (e.g., those that haven't been validated or rejected yet), or exclude on the same thing to get those that have been rejected.

Django form integrityerror for concurrent operation on unique fields

I noticed a thing that I don't know if it is a real issue or I'm doing some wrong design.
I have a model:
class A(Model):
name = CharField(unique=True, max_length=255)
and a modelform linked to A, this modelform has a clean_name() method, that checks if that field is unique in the db (ignore the fact that modelforms already do that by default, I'm specifying that for the example here).
In the view if I do
o = form.save(commit=False)
# xyz
o.save()
and in #xyz I have another client that inserts an A object with the same name field value, o.save() triggers an Integrityerror exception, correctly preventing the duplicate record to be inserted.
What I want to know is how to handle those cases, should I wrap that o.save() with a try/except block and then populate the error field on the form specifying to choose another name value?
This is somewhat a common case that should happen to everyone and that solution is horrible, so I think I'm doing something terribly wrong.
I suspect this might be the case:
Model forms provide uniqueness validation only when a flag is set in xx_clean(). If you override clean with your own (as you have), you need to call the superclass's clean(). See overwriting the clean method.

Django ForeignKey which does not require referential integrity?

I'd like to set up a ForeignKey field in a django model which points to another table some of the time. But I want it to be okay to insert an id into this field which refers to an entry in the other table which might not be there. So if the row exists in the other table, I'd like to get all the benefits of the ForeignKey relationship. But if not, I'd like this treated as just a number.
Is this possible? Is this what Generic relations are for?
This question was asked a long time ago, but for newcomers there is now a built in way to handle this by setting db_constraint=False on your ForeignKey:
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.db_constraint
customer = models.ForeignKey('Customer', db_constraint=False)
or if you want to to be nullable as well as not enforcing referential integrity:
customer = models.ForeignKey('Customer', null=True, blank=True, db_constraint=False)
We use this in cases where we cannot guarantee that the relations will get created in the right order.
EDIT: update link
I'm new to Django, so I don't now if it provides what you want out-of-the-box. I thought of something like this:
from django.db import models
class YourModel(models.Model):
my_fk = models.PositiveIntegerField()
def set_fk_obj(self, obj):
my_fk = obj.id
def get_fk_obj(self):
if my_fk == None:
return None
try:
obj = YourFkModel.objects.get(pk = self.my_fk)
return obj
except YourFkModel.DoesNotExist:
return None
I don't know if you use the contrib admin app. Using PositiveIntegerField instead of ForeignKey the field would be rendered with a text field on the admin site.
This is probably as simple as declaring a ForeignKey and creating the column without actually declaring it as a FOREIGN KEY. That way, you'll get o.obj_id, o.obj will work if the object exists, and--I think--raise an exception if you try to load an object that doesn't actually exist (probably DoesNotExist).
However, I don't think there's any way to make syncdb do this for you. I found syncdb to be limiting to the point of being useless, so I bypass it entirely and create the schema with my own code. You can use syncdb to create the database, then alter the table directly, eg. ALTER TABLE tablename DROP CONSTRAINT fk_constraint_name.
You also inherently lose ON DELETE CASCADE and all referential integrity checking, of course.
To do the solution by #Glenn Maynard via South, generate an empty South migration:
python manage.py schemamigration myapp name_of_migration --empty
Edit the migration file then run it:
def forwards(self, orm):
db.delete_foreign_key('table_name', 'field_name')
def backwards(self, orm):
sql = db.foreign_key_sql('table_name', 'field_name', 'foreign_table_name', 'foreign_field_name')
db.execute(sql)
Source article
(Note: It might help if you explain why you want this. There might be a better way to approach the underlying problem.)
Is this possible?
Not with ForeignKey alone, because you're overloading the column values with two different meanings, without a reliable way of distinguishing them. (For example, what would happen if a new entry in the target table is created with a primary key matching old entries in the referencing table? What would happen to these old referencing entries when the new target entry is deleted?)
The usual ad hoc solution to this problem is to define a "type" or "tag" column alongside the foreign key, to distinguish the different meanings (but see below).
Is this what Generic relations are for?
Yes, partly.
GenericForeignKey is just a Django convenience helper for the pattern above; it pairs a foreign key with a type tag that identifies which table/model it refers to (using the model's associated ContentType; see contenttypes)
Example:
class Foo(models.Model):
other_type = models.ForeignKey('contenttypes.ContentType', null=True)
other_id = models.PositiveIntegerField()
# Optional accessor, not a stored column
other = generic.GenericForeignKey('other_type', 'other_id')
This will allow you use other like a ForeignKey, to refer to instances of your other model. (In the background, GenericForeignKey gets and sets other_type and other_id for you.)
To represent a number that isn't a reference, you would set other_type to None, and just use other_id directly. In this case, trying to access other will always return None, instead of raising DoesNotExist (or returning an unintended object, due to id collision).
tablename= columnname.ForeignKey('table', null=True, blank=True, db_constraint=False)
use this in your program

Categories