Django ModelForm for MultiTableInheritance - django

So I have a model heirarchy using multi-table inheritance and I was wondering if there was a some sort of ModelForm or similar approach for creating an object like a normal model form and have the user choose which of the subclasses they want to use in the form in like a choice field.
The heirarchy is as follow:
class Base(models.Model):
field1 = models.CharField(max_length=10)
field2 = models.CharField(max_length=20)
class A(Base):
pass
class B(Base):
pass
class C(Base):
pass
I can't use Meta abstract models because I have one class that needs a foreign key to the Base which is why I'm using multi-table inheritance and in the future the child classes may have additional field. Do you have any suggestions of how I go about doing this?
Currently the way I'm doing it is using a normal form and having all the common fields in that form as well as a choice field which they can select the type of the model they want to use and then I've created a save method which interprets the type from the choice field and creates the object of that type. I can't help but feel there is a better method of doing this.

Related

Abstract ForeignKey without using GenericForeignKey

To create a generic versioning for my models I have created a model Version:
class Version(models.Model):
version_number = models.IntegerField()
...
and an abstract models VersionedModel
class VersionedModel(models.Model):
...
versions = models.ManyToManyField(Version, related_name="%(app_label)s_%(class)s")
class Meta:
abstract = True
everything works but I would like to have the database check that each version is assigned to one and only one object.
What I can think of is to modify the field versions and use through to create and intermediate table where I could then use a unique index on version_id, but I am just back to the initial problem of creating a ForeingKey field to an abstract model.
I don't like using GenericForeignKey as they create all sort of headaches when working with graphene. I wondered if there is a way to model this in a different way, or to use some constraint I am not aware of, so that the database can provide completeness and uniqueness on its own.

django models-design: "ptr field is required"

I'm using Python 3.6+PostgreSQL 10+latest Django and DjangoRestFRamework. I have the following models, in which several models inherit from a class which is the ForeignKey (One-to-Many) of another class.
class Voteable(models.Model):
Voteable_id = models.BigAutoField(primary_key=True);
class base(Voteable):
class Meta:
abstract = False
class traslated_info(models.Model):
info_about=models.ForeignKey(base)
info_body=models.TextField()
info_language=models.CharField(max_length=2)
class A(base):
A_id=models.BigAutoField(primary_key=True)
A_field=models.TextField()
class B(base):
B_id=models.BigAutoField(primary_key=True)
B_field=models.TextField()
B_belongs_to=models.ForeignKey(A)
class C(base):
C_id=models.BigAutoField(primary_key=True)
C_field=models.TextField()
C_belongs_to=models.ForeignKey(A)
C_belongs_to=models.ForeignKey(B)
Whenever I try saving an object A (via curl), django says that base_ptr is required. I don't know how to model this situation. The end user is not expected to create item base and then item A, B or C. I tried class base as abstract, but an abstract class can't be ForeignKey. I want to automatically create a base class whenever a class A is created.
I think I have two options: A) Remove the ForeignKey and store the language-specific info fields as HStoreField. This makes the code somewhate dependent on Postgree. B) Create some sort of routine that automatically creates parent base item whenever a child A item is created (preserving the one to one relationship).
What do you recommend? Is there some django easy option I'm missing to make option B? I have not found this. Thank you.
Having an autofield as primary_key in the models A, B or C causes this error, as creating a child model doesn't cascade-create parents.
I found two workarounds:
Change autofield option primary_key to false and add
SILENCED_SYSTEM_CHECKS=['fields.E100']
Overriding Viewset create method:
#transaction.atomic
def create(self,request,*args,**kwargs):
request.data['base_ptr'] = base.objects.create(user=request.user,created_date=datetime.utcnow()).pk
return super(viewsets.ModelViewSet,self).create(request,*args,**kwargs)
I will stick with the second, I'm quite sure more issues will arise.
Make your serializer as below, you dont need to create base classes explicitly, it will be created automatically.
class ASerializer(serializers.ModelSerializer):
class Meta:
model = A
read_only_fields = ('base_ptr',)
fields = '__all__'

FeinCMS, intermediate model on content type field

I'm trying to accomplish the following:
class DownloadContentFiles(models.Model):
download_content = models.ForeignKey('DownloadContent')
media_file = models.ForeignKey(MediaFile)
class DownloadContent(models.Model):
files = models.ManyToManyField(MediaFile, through=DownloadContentFiles)
class Meta:
abstract=True
I can see why this doesn't work. Because of the abstract on DownloadContent.
Is there a workaround to specify a intermediate model for contenttype fields?
Generally, if you need more informations when creating a field (such as a list of choices) or a concrete Django model (as you do), you can use initialize_type for that.
class DownloadContent(models.Model):
#classmethod
def initialize_type(cls):
cls.add_to_class('files', ... your model field ...)
The MediaFileContent uses this method to add the type selector:
https://github.com/feincms/feincms/blob/master/feincms/content/medialibrary/models.py#L58
However, in your case this does not work because you'd also have to create the through model dynamically. The reason for that is that for each concrete DownloadContent, you'd need another concrete DownloadContentFiles model.
You could achieve this by using the type built-in to dynamically create new DownloadContentFiles concrete classes (beware of name clashes when using DownloadContent with different CMS bases such as page.Page and elephantblog.Entry).
Maybe a simpler way to achieve what you want:
Add a Downloads model somewhere, and add the files ManyToManyField to this class instead
The DownloadContent only contains a ForeignKey(Downloads)
Yes, you need another model. It might be worth it because you can build a better editing interface for Downloads, and the page editor is also simplified because you only have to select one of the already existing Downloads models for showing them on a page.
Maybe explicitely defining class_name to create_content_type could work for you. Something like this:
class DownloadContentFiles(models.Model):
download_content = models.ForeignKey('MyDownloadContent')
media_file = models.ForeignKey(MediaFile)
class DownloadContent(models.Model):
files = models.ManyToManyField(MediaFile, through=DownloadContentFiles)
class Meta:
abstract=True
Page.create_content_type(DownloadContent, class_name="MyDownloadContent")

Django: Foreign key relation depending on choice

In Django, is there a way to create the following dynamic relationship between models?
class ClothingMale(models.Model):
# male specific fields
class ClothingFemale(models.Model):
# female specific fields
class Person(models.Model):
gender = models.IntegerField(max_length=2, choices=GENDER_CHOICES, blank=True)
clothes = models.ForeignKey(???)
I am using a ModelForm for Person. The clothes fields inside the form should change dynamically depending on which gender was selected. I am aware of GenericForeignKeys but I was wondering if there is better way to solve this?
IMHO it's a question of DB design; I would go for a PersonClothing superclass and subclass it with MalePersonClothing and FemalePersonClothing. The PersonClothing superclass would be a Django abstract base class; a specific class useful when you want to put some common information into a number of other models.
To do it just specify the following option in your model's meta:
class Meta:
abstract = True
One option is to use GenericForeignKey. This of course has int's down-sides, for exampl you cannot filter() on GenericForeignKey
Another option is to use model inheritance, which also has quite a few gotchas.

ModelForm problem

I have declared my model classes as found in this link....I now want to customize how my add/edit ModelForm for a Vehicle object is rendered in that I want the year, make, model, and manufacturer fields to be rendered separately as opposed to referring to the one common_vehicle field from the Vehicle class. How can this be done?
Why don't you make Vehicle inherit CommonVehicle? (Depending on why you've got that FK there, of course -- you may really need it, but I'm guessing not)
Instead of:
class Vehicle(models.Model):
...
common_vehicle = models.ForeignKey(CommonVehicle)
Use:
class Vehicle(CommonVehicle):
...all your other Vehicle fields here, but not the FK to CommonVehicle