I have a model class in Django Parent with a subclass Child. Parent has a boolean field foo which defaults to True that Child inherited. I'd like to migrate my Child class so that
All new Child objects have a default of False for the foo field
All existing Child objects have the foo field set to False
How can I achieve this in Django?
Sometimes the simplest solutions are the best solutions.
You can override the foo field in your Child model:
models.py
class Parent(models.Model):
...
foo = models.BooleanField(default=True)
class Child(Parent):
...
foo = models.BooleanField(default=False)
For existing child instances, you can write a small script to update them. I usually create a view function and trigger it by visiting the associated url (but there are many ways to do this). Something like:
views.py
def update_children_foo_field(request):
for child in Child.models.objects.filter(foo=True):
child.foo = False
child.save()
return JsonResponse({'message' : 'Complete'})
Related
So I'm struggling with ordering the choices within an InlinePanel (for an orderable) on my site. In the admin page, when adding a new item, the options are presented in the order they were added to the site (so, essentially the 'id' for that item); this is less than ideal considering there are hundreds of options presented in a manner that is not user friendly.
I'm assuming this needs to be defined as ordering meta within the orderable, but I can't seem to get it to work. This is what my orderable looks like:
class RelatedPeople(Orderable):
service = ParentalKey('service.Services', related_name='related_person')
person = models.ForeignKey('person.People', null=True, on_delete=models.SET_NULL, related_name='related_service')
panels = [
FieldPanel('person')
]
I've tried the following with no success:
class Meta:
ordering = 'person'
and, trying to append the field within 'person' that I want to sort by, 'name':
class Meta:
ordering = 'person.name'
There must be an obvious way to solve this that I'm over looking. A default sort order of the 'id' (in this case, for 'person.People') is rarely ever going to be suitable from the perspective of the content creator.
Any advice would be greatly appreciated!
Thanks in advance,
Rob
Person model should have:
ordering = ['name']
instead of
ordering = 'name'
And your Orderable object should have it's meta changed to
class Meta(Orderable.Meta):
Via Django Docs, this is the example of abstract base classes, and ordering:
Meta and multi-table inheritance¶
In the multi-table inheritance situation, it doesn’t make sense for a
child class to inherit from its parent’s Meta class. All the Meta
options have already been applied to the parent class and applying
them again would normally only lead to contradictory behavior (this is
in contrast with the abstract base class case, where the base class
doesn’t exist in its own right).
So a child model does not have access to its parent’s Meta class.
However, there are a few limited cases where the child inherits
behavior from the parent: if the child does not specify an ordering
attribute or a get_latest_by attribute, it will inherit these from its
parent.
If the parent has an ordering and you don’t want the child to have any
natural ordering, you can explicitly disable it:
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
When an abstract base class is created, Django makes any Meta inner
class you declared in the base class available as an attribute. If a
child class does not declare its own Meta class, it will inherit the
parent’s Meta. If the child wants to extend the parent’s Meta class,
it can subclass it. For example:
from django.db import models
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
I am not familiar with Wagtail, but can you take a look at this issue :
https://github.com/wagtail/wagtail/issues/4477#issuecomment-382277375
Update:
Maybe you just need to update your Person model like this:
class Person(models.Model):
...
class Meta:
ordering = 'name'
In your files, you try to order RelatedPeople by Person, but what you need is to order the Person list by name in your wagtail dropdown
I have the following model parent
class ModelParent(PolymorphicModel):
company = models.CharField(max_length=50)
.......
and the model child
class ModelChild(ModelParent)
company = models.CharField(max_length=10, blank=True)
...........
how can I make the model child company attribute overwrite the parent company model attribute without making the abstract parent model
This is not possible without an abstract parent model, unfortunately.
Field name “hiding” is not permitted
In normal Python class inheritance, it is permissible for a child class to override any attribute from the parent class. In Django, this isn’t usually permitted for model fields. If a non-abstract model base class has a field called author, you can’t create another model field or define an attribute called author in any class that inherits from that base class.
This restriction doesn’t apply to model fields inherited from an abstract model. Such fields may be overridden with another field or value, or be removed by setting field_name = None.
A recommendation instead would be to simply create a property or rename the child model's field. Another thing you could do is remove the parent model's "company" field and move it to all of the children models instead.
class ModelChild(ModelParent)
child_company = models.CharField(max_length=10, blank=True)
...........
Let's say I have a parent class (Products) and child class related by OneToOneField. Now let's say I have a Products instance object. How will I be able to access all the fields of child ?
class Products(models.Model):
......
class Child1(models.Model):
parent=models.OneToOneField(Products)
......
class Child2(models.Model):
parent=models.OneToOneField(Products)
......
Now let
product_instance=Products.objects.get(id=id)
How can I access child fields without knowing the child class name?
There is package called DeepCollector using this you can get all related objects
from deep_collector.core import DeepCollector
collector = DeepCollector()
collector.collect(Products)
related_objects = collector.get_collected_objects()
try this...
I have a super class like this:
class Superclass(models.Model):
number = models.PositiveIntegerField()
class Meta:
abstract = True
def get_next(self):
return Superclass.objects.get(number=self.number+1)
Now, I have a child class that inherits from the superclass.
What's the problem?
I can't do this: Superclass.objects because the superclass doesn't refer to any database table.
I don't want to query all Superclass childs, only the one of the current child class, like this: When I do instance_of_child1.get_next I don't want to get an instance of Child2.
How to solve this?
My first idea was to add a static constant to any child class that contains the class (So I could do self.myclass.objects) But this seems to be not a good way.
Make the method get_next being part of the child class. Problem: there will be duplicates.
This should work:
def get_next(self):
return self.__class__.objects.get(number=self.number+1)
I am adding a slug to all my models for serialization purposes, so I have defined an abstract base class which uses the AutoSlugField from django_autoslug.
class SluggerModel(models.Model):
slug = AutoSlugField(unique=True, db_index=False)
class Meta:
abstract=True
I also have a custom manager and a natural_key method defined, and at this point I have about 20 child classes, so there are several things that make using an abstract base model worthwhile besides just the single line that defines the field.
However, I want to be able to switch a few of the default arguments for initializing the AutoSlugField for some of the child models, while still being able to utilize the abstract base class. For example, I'd like some of them to utilize the populate_from option, specifiying fields from their specific model, and others to have db_index=True instead of my default (False).
I started trying to do this with a custom Metaclass, utilizing custom options defined in each child Model's inner Meta class, but thats become a rat's nest. I'm open to guidance on that approach, or any other suggestions.
One solution would be to dynamically construct your abstract base class. For example:
def get_slugger_model(**slug_kwargs):
defaults = {
'unique': True,
'db_index': False
}
defaults.update(slug_kwargs)
class MySluggerModel(models.Model):
slug = AutoSlugField(**defaults)
class Meta:
abstract = True
return MySluggerModel
class MyModel(get_slugger_model()):
pass
class MyModel2(get_slugger_model(populate_from='name')):
name = models.CharField(max_length=20)
Update: I started out with the following solution, which was ugly, and switched to Daniel's solution, which is not. I'm leaving mine here for reference.
Here's my Metaclass rat trap that seems to be working (without extensive testing yet).
class SluggerMetaclass(ModelBase):
"""
Metaclass hack that provides for being able to define 'slug_from' and
'slug_db_index' in the Meta inner class of children of SluggerModel in order to set
those properties on the AutoSlugField
"""
def __new__(cls, name, bases, attrs):
# We don't want to add this to the SluggerModel class itself, only its children
if name != 'SluggerModel' and SluggerModel in bases:
_Meta = attrs.get('Meta', None)
if _Meta and hasattr(_Meta, 'slug_from') or hasattr(_Meta, 'slug_db_index'):
attrs['slug'] = AutoSlugField(
populate_from=getattr(_Meta, 'slug_from', None),
db_index=getattr(_Meta, 'slug_db_index', False),
unique=True
)
try:
# ModelBase will reject unknown stuff in Meta, so clear it out before calling super
delattr(_Meta, 'slug_from')
except AttributeError:
pass
try:
delattr(_Meta, 'slug_db_index')
except AttributeError:
pass
else:
attrs['slug'] = AutoSlugField(unique=True, db_index = False) # default
return super(SlugSerializableMetaclass, cls).__new__(cls, name, bases, attrs)
The SlugModel looks basically like this now:
class SluggerModel(models.Model):
__metaclass__ = SluggerMetaclass
objects = SluggerManager()
# I don't define the AutoSlugField here because the metaclass will add it to the child class.
class Meta:
abstract = True
And I can acheive the desired effect with:
class SomeModel(SluggerModel, BaseModel):
name = CharField(...)
class Meta:
slug_from = 'name'
slug_db_index = True
I have to put SluggerModel first in the inheritance list for models having more than one abstract parent model, or else the fields aren't picked up by the other parent models and validation fails; however, I couldn't decipher why.
I guess I could put this an answer to my own question, since it works, but I'm hoping for a better way since its a bit on the ugly side. Then again, hax is hax so what can you do, so maybe this is the answer.