I have a model set up that is
class BasePage(Page):
class Meta:
abstract = True
class PageTypeA(BasePage):
...
class PageTypeB(BasePage):
...
class PageTypeC(BasePage):
...
etc..
How do I create a query to list all of the pages that inherit from BasePage?
If i am understand you properly you need a parent model that have some generic information and other child models need some extra additional information. You can try third-party django-polymorphic link for that. One of important feature that it support Django-Rest-Framework serializer too.
Related
I have two custom Page models that share a field in common, for example:
class CustomPageModelOne(Page):
custom_field = models.IntegerField()
...
class CustomPageModelTwo(Page):
custom_field = models.IntegerField()
...
I need to run, ideally, a single filter across the two types of custom Page models. The Wagtail docs say I can use an exact_type method to specify multiple models inheriting from core Page, so I am trying some variations of the following:
Page.objects.exact_type(CustomPageModelOne, CustomPageModelTwo).filter(custom_field=123)
However, when I try to filter any QuerySet that uses both models, I get an error:
django.core.exceptions.FieldError: Cannot resolve keyword 'custom_field' into field.
How can I query across multiple Wagtail custom Page models that share a field in common?
Note: I have considered creating an abstract class inheriting from Page, but cannot import that abstract model in the file where it is needed.
Abstract class example:
class CustomFieldModel(Page):
custom_field = models.IntegerField()
class Meta:
abstract = True
class CustomPageModelOne(CustomFieldModel):
pass
class CustomPageModelTwo(CustomFieldModel):
pass
As you do Page.objects... you can only filter on fields of the Page model and subclasses of Page
To filter specifically on fields of your CustomPageModelOne, you would have to use CustomPageModel.objects... where that model has that field and both your custom page models are subclasses from
Apparently Page.objects.exact_type is only returning a queryset based upon Page, which makes sense because exact_type has no way of knowing what fields would occur on models descended from Page. I would suggest the following as an alternative approach if re-architecting your models is not an option:
from itertools import chain
model_one_results = CustomPageModelOne.objects.filter(custom_field=123)
model_two_results = CustomPageModelTwo.objects.filter(custom_field=123)
all_results = list(chain(model_one_results, model_two_results))
I we have this models in django:
class FotherModel(models.Model):
# Some fields goes here!
class Meta:
# Some fields goes here!
abstract = True
class ChildModel(FotherModel):
# Some fields goes here!
class Meta(FotherModel.Meta):
#s Some fields goes here!
When we inherit a field from the meta class of Django models, that field appears in child meta class, But this rule does not apply to abstract=True.
I know that if this happens, no table in database will be created, But I don't know how this inheritance didn't happen. Please explain this process for me.
The Model metaclass resets abstract in a model's Meta class. In this document you can see:
Django does make one adjustment to the Meta class of an abstract base
class: before installing the Meta attribute, it sets abstract=False.
This means that children of abstract base classes don’t automatically
become abstract classes themselves.
Also, you can see the source code of this process in this link:
if abstract:
# Abstract base models can't be instantiated and don't appear in
# the list of models for an app. We do the final setup for them a
# little differently from normal models.
attr_meta.abstract = False
new_class.Meta = attr_meta
return new_class
Because of the concept and effect of some fields in meta section in many cases it doesn't make sense that the field is inherited by chidren.
It has been described here
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__'
So I have a base abstract model class
class AbstractBase(models.Model):
category = models.CharField()
// Some fields
class Meta:
abstract = True
Now many classes inherit from this Base. Each category has its own fields
class Category1(AbstractBase):
//some code
class Category2(AbstractBase):
//some code
.
.
.
class CategoryN(AbstractBase):
//some code
Now I do not wish to create separate inlines etc for each of these models.
This is because in the admin only one of these models need to be displayed depending on which category it is.
class CategoryAdminInLine(nested.NestedTabularInline):
model = Category
fields = //some common fields
Is there any easy way to do it?
Try the package django-polymorphic https://github.com/django-polymorphic/django-polymorphic
It adds transparent admin and QuerySet integration for inherited models.
Another solution is to simply remove the abstract=True from the base class.
However, I'm afraid this may not be the right way to do it as it may be copying and duplicating the tables and stuff but works great if the tables won't get too big!..
According to how it's done here.. https://godjango.com/blog/django-abstract-base-class-multi-table-inheritance/
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")