how to handle many to many relationships in modelForms - django

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

Related

Django Using ManyToMany from the other side instead of ForeignKey

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.

Optimal implementation of custom intermediary model

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.

Django admin. Limit choices of many to many field

I have a Many to Many field. I'd like to limit the choices the admin shows in its M2M widget.
I have a model like this:
class A(models.Model):
b_field = models.ManyToManyField(B)
class B(models.Model):
available = models.BooleanField()
How do I limit the B objects shown in the widget only to those who have available = True?
The limit_choices_to option might help you,
Sets a limit to the available choices for this field when this field is rendered using a ModelForm or the admin (by default, all objects in the queryset are available to choose). Either a dictionary, a Q object, or a callable returning a dictionary or Q object can be used.
For eg,
class A(models.Model):
b_field = models.ManyToManyField(B, limit_choices_to={'available': True})

Django Models - Foreign Key of different objects types that share a common base class

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.

Django - Append a related field to a queryset

I have two models:
class A(models.Model):
# fields
class B(models.Model):
a = models.ForeignKey(A)
name = models.CharField(max_length=64)
What I want to do is to get a filtered queryset from A in addition to the related objects from B and append them to the queryset from A, so I would be able to access name field this way: A.B.name
Any idea how to do this?
The problem is that, since the relationship is one-to-many, A doesn't have just one B, but rather a b_set
You could so something like:
for b in a.b_set.all():
b.name
But you can't reference just B because that concept doesn't exist. It would however, if you had used a OneToOneField. Then you could easily do:
a.b.name
Because there's only one B for each A. But, you have to model your object after the actual relationships going on, not how you would prefer the api to work.