Django changing model value based on relationship with other model - python

I'm working on a project that has three models, one of which has a value that is dependent on if the other two are linked to it by a foreign key. the value can also switch if the links change. I was wondering how I would go about that.
For example:
general/models.py
class Person(models.Model):
DEFAULT = 'D'
A = 'A'
B = 'B'
BOTH = 'AB'
TYPES = [
(DEFAULT,'Default'),
(A,'Type A'),
(B,'Type B'),
(BOTH,'Type A & B')
]
# type is default if Person is not linked to A or B
# A if Person is linked to A
# B if Person is linked to B
# BOTH if Person is linked to both
type = models.CharField(max_length=2, choices=TYPES, default=DEFAULT)
A/models.py
class A(models.Model):
person = models.ForeignKey('general.Person',on_delete=models.CASCADE)
B/models.py
class B(models.Model):
person = models.ForeignKey('general.Person',on_delete=models.CASCADE)

You should override the save method of models A and B in order to modify the ForeignKey like this:
class A(models.Model):
person = models.ForeignKey('general.Person',on_delete=models.CASCADE)
def save(self, *args, **kwargs):
if self.person.type == Person.B:
self.person.type=Person.AB
self.person.save()
# elif...

Related

Differences in using a "through" table without or with a related name for many-to-many relationship

When it comes to Many-To-Many fields in Django, what is the difference between using a "through" table like the following...
class A(models.Model):
things = models.ManyToManyField("B", through=ThroughModel)
...
class B(models.Model):
text = models.TextField()
...
class ThroughModel(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
compared to just specifying a related name in the intermediary table like so?
class A(models.Model):
...
class B(models.Model):
text = models.TextField()
...
class ThroughModel(models.Model):
a = models.ForeignKey(A, related_name="things")
b = models.ForeignKey(B)
Either way you should be able to reference the relationship like
a_instance.things
right? Is there some behind the scenes difference between how this is implemented in the database?
The difference lies in how you reference a_instance from b_instance:
b_instance.a_set
in the first case and
b_instance.things
in the second case

How do you get a field related by OneToOneField and ManyToManyField in Django?

How do you get a field related by OneToOneField and ManyToManyField in Django?
For example,
class A(models.Model):
myfield = models.CharField()
as = models.ManyToManyField('self')
class B(models.Model):
a = models.OneToOneField(A)
If I want to get a 'myfield' and all associated 'as' using class B, given a 'myfield' equal to a string like 'example', how is it done?
Models.py
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __str__(self): # __unicode__ on Python 2
return "%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
primary_key=True,
)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
def __str__(self): # __unicode__ on Python 2
return "%s the restaurant" % self.place.name
Let create a place instance.
p1 = Place.objects.create(name='Demon Dogs', address='944 W. Fullerton')
Then create a restaurant object.
r = Restaurant.objects.create(place=p1, serves_hot_dogs=True, serves_pizza=False)
Now, to access place from Restaurant:
>>> r.place
<Place: Demon Dogs the place>
vice-versa to access Restaurant from place
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>
I did not understand the many-to-many field part can you please elaborate?
First you get an instance of B say b and you can easily access myfield and as through the a attribute of b
b.a.myfield
b.a.as.all()
Furthermore, CharField requires a max_length attribute as follows:
class A(models.Model):
myfield = models.CharField(max_length=128)
as = models.ManyToManyField('self')
class B(models.Model):
a = models.OneToOneField(A)
A general point, give more descriptive names to your models and their attributes, or at the very least, add comments explaining what these models represent

How to add conditional fields to ModelAdmin in Django?

I have model classes:
class Product(models.Model):
category = models.ForeignKey(Category)
name = models.CharField(max_length=90)
...
class Category(models.Model):
name = models.CharField(max_length=90)
description = models.CharField(max_length=2000)
properies = models.ManyToManyField(Property)
...
#property type, ex: 'weight', 'length'
class Property(models.Model):
...
#value for every product
class PropertyValue(models.Model):
product = models.ForeignKey(Product)
property = models.ForeignKey(Property)
...
and I need custom product/add/ page, having PropertyValue forms set depends on chosen category.
I've made a method getting PropertyValue list by category_id in ModelAdmin class, but how can I call it in runtime when chosen category changes? Is it possible in django?
What do you mean when you said in runtime. If those categories change, the new records will apear every time you load the add pages.
Did you do yor form class? Kind of:
class PropertyValueForm(forms.Form):
product = forms.ModelChoiceField(queryset=Product.objects.all())
property = forms.ModelChoiceField(queryset=Property.objects.all())
Or:
def getProduct():
# DO YOUR STUFF
return product_list
class PropertyValueForm(forms.Form):
product = forms.ChoiceField(choices=get_my_choices())

(Django) define `__str__` in a model with attributes of other model not yet defined

For Django 1.9, I implemented two simple models:
class A(models.Model):
def __str__(self):
# TO IMPLEMENT: Get the name of newest model B associated with A
class B(models.Model):
name = models.CharField(max_length=50)
date = models.DateField()
to_a = models.ForeignKey(A)
def __str__(self):
return name
Model A is for a group of model B (by ForeignKey to_a.)
I want to make __str__ of model A to be the name of B which has newest date among those who are related to given A with to_a.
For example, if we have two A : [A1, A2] and three B : [("1", 2014-01-01, A1), ("2", 2015-01-01, A2), ("3", 2015-12-19, A1)], then A2.__str__() should return "3".
How can I implement A.__str__?
Something like this will help you
self.b_set.last().name

Data models dependencies, can I simplify these objects?

Essentially I have 6 object models, 3 for my regular objects and 3 for the dependencies of those objects. Individually, these objects can be dependent on one or multiple instances of each of the 3 regular objects.
My Question:
Is this best practice? I essentially want to be able to add a new dependency to a regular object when needed. For example:
a = A.objects.get(id=1)
adependency = ADependencies.objects.get(dependentTo=a)
Then, I'd have an object with all of the dependencies for a.
Alternatively, I did think of a way to merge the 3 dependency objects into one; however, I'm unsure whether it's good practice.
class Dependencies(models.Model):
id = models.CharField(max_length=16)
dependentTo = CharField(max_length=16)
a = models.ManyToManyField(A)
b = models.ManyToManyField(B)
c = models.ManyToManyField(C)
In this scenario, I don't use a ForeignKey to map the dependentTo object. Instead, I would use the object id to pull the object - allowing me to be class agnostic; however, this would require unique ids throughout the 3 regular objects.
a = A.objects.get(id=1)
adependency = ADependencies.objects.get(dependentTo=a.id)
One more idea!
Is it possibly to still use ForeignKey's, but pass in a string with the class name instead?
class Dependencies(models.Model):
id = models.CharField(max_length=16)
type = models.CharField(max_length=16)
dependentTo = ForeignKey(type)
a = models.ManyToManyField(A)
b = models.ManyToManyField(B)
c = models.ManyToManyField(C)
Object Models:
class A(models.Model):
id = models.CharField(max_length=16)
title = models.CharField(max_length=32)
summary = models.CharField(max_length=256)
class B(models.Model):
id = models.CharField(max_length=16)
title = models.CharField(max_length=32)
summary = models.CharField(max_length=256)
a = models.ForeignKey(A)
class C(models.Model):
id = models.CharField(max_length=16)
title = models.CharField(max_length=32)
summary = models.CharField(max_length=256)
b = models.ForeignKey(B)
class ADependencies(models.Model):
id = models.CharField(max_length=16)
dependentTo = models.ForeignKey(A)
a = models.ManyToManyField(A)
b = models.ManyToManyField(B)
c = models.ManyToManyField(C)
class BDependencies(models.Model):
id = models.CharField(max_length=16)
dependentTo = models.ForeignKey(B)
a = models.ManyToManyField(A)
b = models.ManyToManyField(B)
c = models.ManyToManyField(C)
class CDependencies(models.Model):
id = models.CharField(max_length=16)
dependentTo = models.ForeignKey(B)
a = models.ManyToManyField(A)
b = models.ManyToManyField(B)
c = models.ManyToManyField(C)
Thanks!
This can be done a lot simpler. Define a Dependency model instead of a Dependencies model. You will need the contenttypes django contribution:
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Dependency(models.Model):
dependent_contenttype = models.ForeignKey(ContentType)
dependent_id = models.PositiveIntegerField()
dependent = GenericForeignKey('dependent_contenttype', 'dependent_id')
prerequisite_contenttype = models.ForeignKey(ContentType)
prerequisite_id = models.PositiveIntegerField()
prerequisite = GenericForeignKey('prerequisite_contenttype',
'prerequisite_id')
Then you can make dependencies a reverse generic relation on models A, B and C if you like:
class A:
# ...
dependencies = GenericRelation(
Dependency,
content_type_field='dependent_contenttype',
object_id_field='dependent_id')
I think inheritance may simplify your data structures a lot.
Let's leave model A as-is:
class A(models.Model):
id = models.CharField(max_length=16)
title = models.CharField(max_length=32)
summary = models.CharField(max_length=256)
Your classes B and C has those fields of A plus one additional, so we may rewrite that as
class B(A):
a = models.ForeignKey(A)
class C(A):
b = models.ForeignKey(B)
Now as we have one base class, we only need one dependency class:
class ADependencies(models.Model):
id = models.CharField(max_length=16)
dependentTo = models.ForeignKey(A)
dependents = models.ManyToManyField(A)
Now you can set any of A, B and C objects to dependentTo and dependents. If you only need the main object from dependency, the object of type A will have either attribute b, attribute c or none of them. You may also query on those attributes:
ADependencies.objects.filter(dependentTo__b__isnull=False)
This structure is more scalable and more easily maintainable because if you need to add one more model, you only need to write the unique code for it and don't have to deal with dependency classes.
One more way to simplify your models is to just have one model:
class A(models.Model):
id = models.CharField(max_length=16)
title = models.CharField(max_length=32)
summary = models.CharField(max_length=256)
a = models.ForeignKey(A, null=True)
This way you only have model A and you may leave field a empty (indication that it is just a simple A instance) or set the value of a (it will mean the object of type B or C). Then your dependency class is the same as in previous example, but you don't need to deal with those backwards relations to test for true object type.
If you really need to disinguish between B and C objects you may write your A class like this:
class A(models.Model):
A = 0
B = 1
C = 2
TYPE_CHOICES = (
(A, "A"),
(B, "B"),
(C, "C")
)
id = models.CharField(max_length=16)
title = models.CharField(max_length=32)
summary = models.CharField(max_length=256)
a = models.ForeignKey(A, null=True)
obj_type = models.IntegerField(choices=TYPE_CHOICES)
This way you have just one model class and one dependency class and can tell what type the object is by checking obj_type. Also you should implement some check to prevent cases where a is not null and obj_type is A and similar.
Let me know if this solution is what you need.

Categories