class C:
attribute_c = models.CharField()
class B:
class_a = models.ForeignKey(A)
class_c = models.ForeignKey(C)
foo = models.SomeField(null=True)
boo = models.SomeOtherField(null=True)
class A:
items = models.ManyToManyField(C, through='B')
attribute_a = models.CharField()
attribute_b = models.IntegerField()
attribute_c = models.DateField()
attribute_d = models.ForeignKeyField()
[...]
To create a relation between class A and class C, the method save_m2m() can be called in the forms. However since a custom relation is being used (class B), you have to create it yourself, by calling B.objects.create(instance_of_a, instance_of_b).
How can I create this relation without having to manually do it in Forms.py? Maybe overriding the add() method (A.items.add()) to remove a validation Django does that subsequently throws the error below?
Cannot set values on a ManyToManyField which specifies an intermediary
model. Use app.B's Manager instead.
I have classes that are very similar to the ones I wrote above. They have been working fine until now, when some requirements changed. These requirements forced me to introduce a custom intermediary class, adding the attributes foo and boo.
I have a ton of tests using FactoryBoy and thus creating instances of class A without using forms. If I have to manually insert this relation everytime I create an instance of class A, it will be a pain.
What is the best way to circumvent this problem?
I basically need to have the relation between A and C created without being bothered to fulfill these attributes on B. I can do that through a signal or something.
PS: I'm using Django 1.11.17.
Related
In a specific Django app, I have a DB Model class A that is considered as the main class and many other models are connected to it weather through one to one relationship (like B) or one to many relationship (like C). So, the direct approach to implement such is:
class A(Model):
b = OneToOneField('B', on_delete=CASCADE)
# other fields
class B(Model):
# some fields
class C(Model):
a = ForeignKey(A, on_delete=CASCADE)
# other fields
Now, when I want to create a new object of A, the sequence will be:
create B object
create A object and link the created B object to it
create C object and link the created A object to it.
This happens on a larger scale with a lot of models linked with model A. But I want to have all the relations seen in the A class so that when I want to create an object of A, I go to create all the related objects first after validating them regardless the relationship, then create new A object and link all those related objects to it. So, I did so:
class A(Model):
b = OneToOneField('B', on_delete=CASCADE)
c = ManyToManyField('C') # as there is no OneToManyField
class B(Model):
# some fields
class C(Model):
# some fields
But this solution seems not good as C should have only one A object.
Is it acceptable to do this or is there another good practice approach to do?
OneToMany is equal to ForeignKey constructor.
class A(Model):
b = OneToOneField('B', on_delete=CASCADE)
c = Foreignkey('C', on_delete=CASCADE)
class B(Model):
# some fields
class C(Model):
# some fields
seems totally fine approach to me.
Suppose there are total 3 class. A,B and C.
class A(models.Model):
one = models.IntegerField()
two = models.IntegerField()
three = models.IntegerField()
class Meta:
abstract = True
class B(A):
pass
class C(A):
pass
I am inheriting the class A in B and C,but i want to use only fields one and two in classB while all the three fields in classC.
Is it possible to inherit some fields of classA in classB and some in classC?
or is it a bad idea?
As you may already know, there are three types of inheritance across models in django.
Often, you will just want to use the parent class to hold information that you don’t want to have to type out for each child model. This class isn’t going to ever be used in isolation, so Abstract base classes are what you’re after.
If you’re subclassing an existing model (perhaps something from another application entirely) and want each model to have its own database table, Multi-table inheritance is the way to go.
Finally, if you only want to modify the Python-level behavior of a model, without changing the models fields in any way, you can use Proxy models.
The only choice for your use-case is abstract base classes.
And the thing you are looking for from docs:
Fields inherited from abstract base classes can be overridden with another field or value, or be removed with None.
So you should have:
class A(models.Model):
one = models.IntegerField()
two = models.IntegerField()
three = models.IntegerField()
class Meta:
abstract = True
class B(A):
three = None
class C(A):
three = None
And to answer your second question, It's not a bad idea; We normally use it when we want to change the USERNAME_FIELD while extending django's default user model.
Let me explain. I have 2 tables which are child classes of another abstract table. The abstract table has a relationship to a model called Foo. The related_name is set dynamically. The code looks like this:
class Foo(models.Model):
...
class Parent(models.Model):
foo = models.ForeignKey(
Foo,
on_delete=models.CASCADE,
related_name='%(app_label)s_%(class)s_related'
)
...
def bar(self):
print('bar')
class Meta:
abstract = True
class ChildOne(Parent):
...
class ChildTwo(Parent):
...
Therefore, the related names become 'myapp_childone_related', and 'myapp_childtwo_related'.
Now, lets say I want to call the bar() method of all the objects from the ChildOne and ChildTwo model that is related to a Foo object. There is a catch though, I want to it from with a class method of the Foo model. Currently, I'm doing it like this:
class Foo(models.Model):
...
def call_bar(self):
references = ('childone', 'childtwo')
for ref in references:
children = getattr(self, f'myapp_{ref}_related').all()
for child in children:
child.bar()
This works fine, but honestly feels a bit hack-y, especially when dealing with more than two children classes. Is there a nicer, more Pythonic solution to this problem?
Edit: I decided not to mention previously that I wanted to call the bar() method from within a class method of the Foo model because I thought that it was unnecessary for this question. However, Daneil Roseman's answer suggested making a list of classes, which is a good solution, but it would not work within the class method, as the classes have not yet been defined at that point in the module. So mentioning that in this edit.
A related_name is only syntactic sugar for performing a query from the related class itself. So you should just do this explicitly:
child_classes = [ChildOne, ChildTwo]
for child_class in child_classes:
children = child_class.objects.filter(foo=foo)
I have the following conceptual design in mind for one of my models.
class A(models.Model):
...
class B(A): #Inherits A
fieldA = ...
fieldB = ...
class C(A): #Inherits A
fieldC = ...
fieldD = ...
class D(models.Model):
field = models.ForeignKey(A) #Here lies the problem, should store B or C
Given the models above, I'd like to store a foreign key to either B or C in D but not both.
I tried setting the Meta class property of A to abstract but that doesn't allow a ForeignKey relationship to A. I do not want to ever have an instance of A that isn't B or C, but if necessary, I can restrict this behavior with the save signal.
Is there an easier design that would allow me to store a foreign key from a list of types where all classes inherit from a common base?
I can think of two options:
Use a generic relation in your D class instead of a foreign key.
If you don't need to filter D using specific fields from B or C you could continue with the approach you have now, but add a method to D that would retrieve the child class of field:
class D(models.Model):
field = models.ForeignKey(A)
def get_field(self):
try:
return self.field.b
except B.DoesNotExist:
pass
try:
return self.field.c
except C.DoesNotExist:
pass
This definitely has some performance implications and as you said in your post, you would have to manually ensure that every instance of A has a B or C subclass. Obviously this approach doesn't scale well if you are going to have n number of subclasses.
I have a model similar to this one:
class A(models.Model):
name = models.CharField(primary_key=True)
class B(models.Model):
(morefields)
target = models.ManyToManyField(A,through='C')
class C(models.Model):
a_key = models.ForeignKey(A)
b_key = models.ForeignKey(B)
(extra fields)
I am creating a form to edit an item of B using a modelForm. However, I get "Cannot set values on a ManyToManyField which specifies an intermediary model" error. If I exclude the target field it works fine.
Could you suggest any way to workaround this?
You can use inlines. The problem is that Django can't create the relation for you because there's additional fields that must be set on the join table (your "through" model). Try the following:
class CInlineAdmin(admin.TabularInline):
model = C
extra = 1
class BAdmin(admin.ModelAdmin):
inlines = [CInlineAdmin,]
By your own mentioning above, you have (extra fields) in Class C. How is django supposed to populate those extra fields, if by using given A, you want to create more B's.
You should probably create an admin for C, where in you can add each A and B to a given C