Inherited model with FK to base model overwrites base reference - django

I have a situation where I have 2 models, with the second model (B) being a subclass of the first (A), and also having a (different) 1-to-1 reference to another instance of same parent model (A). The objective is to have some special cases of A linked to other instances of A's in a new table, B's. For reasons I won't get into here, these need to be 2 different models (ie I can't add a nullable 1-to-1 reference field on A). This is illustrated below.
class A(models.Model):
name = models.CharField()
class B(A):
reference = models.OneToOneField(A)
However now when I try instantiate B with a reference to a different A, it doesn't work. Consider the following:
>>> a1 = A(name='a1')
>>> a1.save()
>>> b1 = B(name='b1', reference=a1)
>>> b1.save()
>>> b1.id
1
>>> b1.reference.id
1
Or alternatively:
>>> a1 = A(name='a1')
>>> a1.save()
>>> b1 = B(name='b1')
>>> b1.save()
>>> b1.reference = a1
>>> b1.save()
>>> b1.id
2
>>> b1.reference.id
2
Whereas what I would like here is for b1.id to equal 2 and b1.reference.id to equal 1 (referencing a1).
What is going on here? Why can't I have independent references to the base instance with the ptr_id and a different entry in the same table, in the reference field?

Related

How to automatically choose related model using field value

Assume, we have model
class BaseModel(models.Model):
is_a = models.BooleanField()
and two models related to this one:
class A(models.Model):
value_1 = models.IntegerField()
base = models.ForeignKey(BaseModel, related_name='a')
class B(models.Model):
value_1 = models.IntegerField()
value_2 = models.IntegerField()
base = models.ForeignKey(BaseModel, related_name='b')
What I need is to refer to A or B depending on is_a property.
For example,
base = BaseModel.objects.get(id=1)
if base.is_a:
obj = A.objects.create(value_1=1, base=base)
else:
obj = B.objects.create(value_1=1, value_2=2, base=base)
return obj
or
if base.is_a:
queryset = base.a.all()
else:
queryset = base.b.all()
return queryset
i.e., every time I have to check the is_a property.
Is there more graceful way?
There are two only related models, A and B, no other ones will appear in the nearest future.
Part of the problem can be solved with django-polymorphic, e.g.:
class A(PolymorphicModel):
...
class B(A):
...
This allows to retrieve all A's and B's with one request like base.b.all(), but the problem here is that every B creates instance of A, which is unwanted.
I've considered GenericForeignKey as well. As far as I understood it has a number of limitations like "1) You can't use GenericForeignKey in query filters ; 2) a GenericForeignKey won't appear in a ModelForm" (from GenericForeignKey or ForeignKey).
One idea is to add choices to the BaseModel to have a string representation of your boolean value. If you set the strings equal to the A and B model names, you can use the model.get_foo_display() method to return the name of the model. Then use the Python getattr() method to access attributes as variables.
class BaseModel(models.Model):
base_model_choices = (
(True, 'A'),
(False, 'B'),
)
is_a = models.BooleanField(choices=base_model_choices)
For example,
base = BaseModel.objects.get(id=1)
queryset = base.getattr(models, get_is_a_display()).all()
obj = getattr(models, get_is_a_display()).objects.create(base=base)

Why is the result of code snippt is not true according the define about classmethod in python(2.7)?

class Pizza(object):
radius = 2
#classmethod
def get_radius(self):
return self.radius
>>>print Pizza.get_radius is Pizza().get_radius
False
I think the result is True,because the classmedthod belongs to the class object.
When you instanciate Pizza you get a get_radius function which has a different id but points to the same code:
>>> id(Pizza().get_radius)
50027464
>>> id(Pizza.get_radius)
41275656
ok the refs are different but the contents are the same:
>>> Pizza.get_radius.__func__ is Pizza().get_radius.__func__
True
the function objects are the same and using == also yields True:
>>> Pizza().get_radius == Pizza.get_radius
True
So, like when comparing strings or integers, or whatever, it's better to avoid is because it's too much implementation dependent, with minimal benefits (except for the singletons like None)
>>> Pizza.get_radius()
2
>>> Pizza().get_radius
<bound method type.get_radius of <class 'Pizza'>>
When you type Pizza.get_radius(),you call the function and you get your result.
When you type Pizza().get_radius ,first of all you initiate a new Pizza object and you don't call the function really.

Set value in related model with ManyToManyField

I have these four models all related by ManyToManyFields:
A <--> B <--> C <--> D
Given an A object I need to set a value in its related field in D.
class A(models.Model):
name = models.CharField()
# Get all foo, from differents B, C and D
#property
def get_all_foo(self):
for b in B.objects.filter(...):
for c in C.objects.filter(...):
foo = ';'.join([d.foo for d in D.objects.filter(...)])
return foo
def set_foo(self, value):
# the code I need
class B(models.Model):
a = ManyToManyField(A)
class C(models.Model):
b = ManyToManyField(B)
class D(models.Model):
c = ManyToManyField(C)
foo = models.TextField()
What I need is to set the value of foo in D from a function in A (the same way I get all the values from it). How could I do it?
Firstly, your get_all_foo method is incredibly inefficient, potentially causing thousands of separate database calls. Replace it with:
return ';'.join(D.objects.filter(c__b__a__=self).values_list('foo'))
You can use the same principle to update the D objects from A:
D.objects.filter(c__b__a__=self).update(foo=value)

Django: Assign a model object as a ForeignKey before saving

I have two models
class A(models.Model):
title = models.CharField()
class B(models.Model):
heading = models.ForeignKey(A)
content = models.CharField()
When I tried to do the below I'm getting IntegrityError: b.heading may not be null
b = B()
a = A()
b.heading = a
b.heading.title = 'title'
b.content = 'content'
b.heading.save()
b.save()
Why is this happening? can't we work with objects this way?
This actually has to do with the order you do things.
When first assigning a to b.heading, the A object is not yet saved and doesn't have a primary key. Thus, on assignment, b.heading_id will remain None. b.heading doesn't know anything about b, so when saved, b.heading_id will remain None. When saving b, it expects b.heading_id to have a valid non-null value, which it obviously hasn't. Therefore, an IntegrityError is raised.
What you should do, is do the assignment after saving a:
b = B()
a = A()
a.title = 'title'
a.save()
b.heading = a
b.content = 'content'
b.save()

Remove mutable objects from list

So I have the following class object:
class Bond(object):
def __init__(self, Atom1=None, Atom2=None):
self.atoms = [Atom1, Atom2]
where Atom1 and Atom2 are mutable objects.
and I have:
>>> first_bond
Bond(Atom1, Atom2)
>>> second_bond
Bond(Atom1, Atom3)
>>> third_bond
Bond(Atom2, Atom1)
and also have:
>>> bonds
[first_bond, second_bond, third_bond]
If you realize, the first_bond and third_bond are the same since one is the reverse of the other, this is:
>>> first_bond == third_bond[::-1]
True
So my question is how can I implement a function or something that can filter only distinct objects, so that my final bonds is:
>>> bonds
[first_bond, second_bond]
I have read that maybe using __eq__ and __hash__ method would be a solution, and then using set(bonds). But since Atoms are mutable objects I don't know if this is kind of possible.