I have a parent class:
class Parent(models.Model):
field1 = model.CharField()
field2 = model.CharField()
And a child:
class Child1(Parent):
pass
Is there a possible way to create a child object whenever a perent is saved?
The child inherits all the fields from the parent, but, regardless if filled or not, I would need to create a new child object whenever a parent is saved.
Any ideas?
You can use signals or you can override save method of Parent model to do that.
#receiver(models.signals.post_save, sender=Parent)
def post_parent_save(sender, instance, created, *args, **kwargs):
# Create child here
Or,
class Parent(models.Model):
def save(self, *args, **kwargs):
super(Parent, self).save(*args, **kwargs)
# Create child here
In both options, if you want to create a child only when a parent is created (not updated), you need to add extra login. For signals, you can use the created parameter, for overriding save method, you need to check if the model instance has an id field before calling super save method.
I'm wondering if something like this would work for you:
class Child1(Parent):
class Meta:
db_table = 'whateverappparentisin_parent'
managed = False
I'm not sure what Django would do with this, but the idea is that you get a model with the exact same fields, backed by the same database table (so everything else e.g. deletes on Parent would also immediately be "visible" on Child1), without Django wanting to make migrations for it.
But I don't know if it's allowed.
Related
Let's say I have a class something like the following:
class PostSerializer(serializers.HyperlinkedModelSerializer):
updated_at = serializers.DateTimeField()
def __init__(self, *args, **kwargs):
init = super().__init__(*args, **kwargs)
return init
I want to create a subclass of the PostSerializer class and I'd like to remove the updated_at constant property from the subclass-ed class.
class PostWithoutUpdatedAtSerializer(serializers.HyperlinkedModelSerializer):
# something to remove the updated_at property ?
def somefunc(self);
pass
I use a framework for example django so generally I cannot simply remove the property from the parent class, I need to subclass them. And of course obviously I need to "delete" the property, I cannot do updated_at = None, it's not a deleting.
How is it possible? Thanks.
It's not directly possible, since the attribute doesn't exist on your derived class at all (it does on the superclass), so there's nothing to remove or reassign.
Instead, the framework you're using (Django REST Framework, my magic ball tells me), uses a metaclass that inspects the class definition for field objects and puts them into cls._declared_fields on the class (along with any fields from the superclass(es)).
The real fields for your serializer instance are acquired by get_fields(), which by default just copies _declared_fields.
In other words, if your Django REST Framework serializer subclass should not serialize that field, customize get_fields():
def get_fields(self):
fields = super().get_fields()
fields.pop("updated_at", None) # remove field if it's there
return fields
This issue was raised several times, though I guess I'm getting it from a different reason, or at least I can't tell how it's related.
Django: 1.10.5, Python: 3.5.2, Postgres: 9.5
So, I have such models (simplified):
class Parent(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
date_created = models.DateTimeField(auto_now=True)
class Child(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
date_created = models.DateTimeField(auto_now=True)
parent = models.ForeignKey(Parent)
def __init__(self, parent, *args, **kwargs):
super(Child, self).__init__(*args, **kwargs)
self.parent = parent
I have a custom queryset:
class ChildQuerySet(models.query.QuerySet):
def find_recent(self):
return self.order_by('-date_created')
Child.objects = ChildQuerySet.as_manager()
Then, I'm trying to test some custom queryset method for child:
class ChildQuerysetDatabaseIntegrationTest(TestCase): # from django.test
default_parent = Parent()
def setUp(self):
super(ChildQuerysetDatabaseIntegrationTest, self).setUp()
self.default_parent.save()
def test_find_recent(self):
# given
for _ in range(1, 10):
child = Child(self.default_parent)
child.save()
# when
recent = Child.objects.find_recent()
ordered = Child.objects.order_by('-date_created')
# then
self.assertEqual(list(ordered), list(recent))
This produces following error on the last line of test (fetching all entities):
ValueError: Cannot assign "UUID('...')": "Child.parent" must be a "Parent" instance.
Usually, when there's some mapping error, the error is thrown during entity saving, but here everything seems to persist successfully, but then fails on retrieval.
The UUID object that is tried to be assigned to parent instance, is actually child's id object, which makes me even more confused.
I tried changing object creation to Parent.objects.create(), but the result didn't change. Calling any function that retrieves a Child object, like ordered.first(), also fails, so I have no clue what's happening.
The problem is your custom constructor for Child. When the ORM is trying to retrieve results, the overridden constructor prevents the ORM from passing in the column values to instantiate the instance properly. In other words, the ORM is trying to pass in the values in the column-specified order, e.g.,
Child(id, date_created, parent)
while the custom constructor expects values to be passed in the following order:
Child(parent, . . .)
To resolve this issue, remove your custom constructor and use
instance = Child(parent=parent)
whenever you want to initialize a child with a parent.
I have two Models, one of them has a ForeignKey to the other, the idea is to save them inside a transaction but it gives me an error.
These are my models:
class Parent(models.Model):
name = models.CharField(...)
...
class Child(models.Model):
parent = models.ForeignKey(Parent)
...
this is my view
#transaction.atomic()
def save_parent(request):
try:
parent = Parent(name=request.POST.get('name'),other_fields).save()
child = Child(parent=parent,other_fields).save()
...
except:
pass
I have looked for transaction savepoints but I dont understand them.
My main goal is to save both or don't save anything
Any Ideas?
You are not saving the objects correctly. Try this
parent = Parent(name=request.POST.get('name'),other_fields)
parent.save()
child = Child(parent=parent,other_fields)
child.save()
Or use the create method inside the manager.
parent = Parent.objects.create(name=request.POST.get('name'),other_fields)
child = Child.objects.create(parent=parent,other_fields)
I have a custom django field subclass for storing my own pickled classes. Is there any way I could set a model attribute that points to the model instance on my pickled class on each load from the database?
So far my best guess is in the unpickling process, inside the to_python method, but I'm not sure if the Field has a reference to the model instance or class.
EDIT 1: The model reference inside of the to_python method is indeed a reference to the class, not the instance
Figured it out!
I overrode the model's __init__ method like this:
class MyModel(models.Model):
def __init__(self, *args, **kwargs):
# Don't do any extra looping or anything in here because this gets called
# at least once for every row in each query of this table
self._meta.fields[2].model_instance = self
super(MyModel, self).__init__(*args, **kwargs)
field1 = models.TextField()
field2 = models.PickleField()
field3 = models.DateTimeField()
Then in my field subclass:
def to_python(self, value):
# logic and unpickling, then right before your return:
if hasattr(self, 'model_instance'): # avoid AttributeError if list, dict, etc.
value.model_instance = self.model_instance
return value
I have a base class that is a Django form. The child class then inherits from the parent including all the fields, but I need to make changes to one of the parent's field option such as label, required, and etc.
Example:
class BaseForm(forms.Form):
userid = forms.CharField(required=True)
class ChildForm(BaseForm):
# I need to change the parent field option
userid = forms.CharField(required=False)
Any suggestions?
You're doing exactly what you should do.
It's particularly fitting in this case because that's the exact pattern for overriding ModelForm fields.
If you need to retain properties you don't know about / are outside your control (or what have you), you could override the __init__ method and access the form fields via self.fields['myfield']
class ChildForm(BaseForm):
def __init__(self, *args, **kwargs):
super(ChildForm, self).__init__(*args, **kwargs)
self.fields['userid'].required = False