Django Related Object empty queryset on clean() - python

I'm trying access a list of related objects through the "related_set.all()" method whenever a model is saved from Django Admin. No matter what I do however the QuerySet is always empty... Here's what it looks like (I've removed a lot of (hopefully) irrelevant stuff)
class Board(models.Model):
group = models.OneToOneField(Group, on_delete=models.CASCADE)
def clean(self):
roles = self.role_set.all() # This just returns <QuerySet []>
... validation, etc....
class Role(models.Model):
board = models.ForeignKey(Board, on_delete=models.CASCADE)
members = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
Funny thing is that it used to work, but after setting the User to be the AUTH_USER_MODEL (after a lot of refactoring) it suddenly stopped returning anything in the QuerySet... For what it matters I'm using a NestedStackedInline in the Admin panel, and what I hope to achieve is that whenever the "Save"-button in the admin panel is called, it should call the clean() method on the Role-class, call the clean() method on the Board-class and upon success call the save() method on both.
I've been troubleshooting this for way too long now and would really appreciate if anyone have some pointers or ideas.

Related

Django Custom Manager to dynamically filter archived objects

Suppose I have a model:
class Car(models.Model):
name = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
When I query on Car, I always want to return the objects satisfying, is_active=True.
For this, searching on StackOverFlow, I get that my best bet is to use ModelManager, like this:
class CarManager(models.ModelManager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
And, use this Manager in my model.
class Car(models.Model):
name = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
objects = CarManager()
Using this solution always returns active Car queryset.
But, sometimes I want to return inactive Car queryset as well, and I don't want to write another ModelManager.
To elaborate,
When I run,
Car.objects.all()
or,
Car.objects.filter(name__contains='Car')
or,
Car.objects.filter(is_active=True)
I only want active Car queryset.
When I run,
Car.objects.filter(is_active=False)
I want to have inactive Car queryset.
And, I want to achieve this using single ModelManager and default methods (get, filter, all, etc). Why I want this is because, it has been used in many places already.
So, is there any way to achieve this? Any suggestions or insights are heartily welcome.
Thanks for your help in advance.
So, after a long research and going through documentations and Django source code, I have come up with this for filter() method:
class CarManager(models.ModelManager):
def filter(self, *args, **kwargs):
if kwargs.get('is_active') == False:
return super().get_queryset().filter(*args, **kwargs)
return self.get_queryset().filter(*args, **kwargs)
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
Here, I have overriden filter() method, so that:
If is_active=False is passed, then the parent (default) get_queryset() followed by filter() is called.
If is_active is not passed or is_active=True is passed, then the overridden get_queryset() method is called (which returns the active Car queryset) followed by the filter() method.
If there are any other solutions or better practices, please do mention them. Thanks.
I am afraid is not possible since you are overwriting the manager's base queryset. What you could do instead of creating another manager is to implement an extra method that returns a queryset with only inactive cars, something like:
class CarManager(models.ModelManager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
def get_inactive_cars(self):
return super().get_queryset().filter(is_active=False)
And then replace the querysets where you retrieve the inactive ones:
Car.objects.get_inactive_cars()

How can I implement a verified field in django?

In Django I'd like to add a field "verified" of type BooleanField to my models which shall indicate if the current model instance has been reviewed by a staff user member or not. Whenever a model instance field other than the "verified" field changed the verified field value shall be reset to False. Whenever only the "verified" field has been changed it's value shall be taken as is (most of the time True but potentially False as well).
One possibility would be to reset the "verified" field in post-save signals handlers considering update_fields passed to save(). However using signals seems to be considered an anti-pattern in almost all use cases. Instead one should override the save() method. But still when overriding save I'd have to determine update_fields manually somehow. Otherwise I've no information about which fields changed.
How can I implement something like this most easily. I'd prefer a solution using a third-party package w.o. custom hacks or a solution without any dependencies to other packages. However using django-model-utils monitorfield, django-dirtyfields for a custom implementation or something equivalent would be ok as well.
Using dirty-fields seems to be easiest to implement a verified field. So far I came up with something like follows:
DJANGO-APP/models.py:
from django.db import models
from dirtyfields import DirtyFieldsMixin
class VerifiedModel(DirtyFieldsMixin, models.Model):
"""
Abstract class which allows to extend models with user verification model field.
"""
ENABLE_M2M_CHECK = True
verified = models.BooleanField(
default=False,
help_text="The verification status. True means meta-data is verified. False means meta-data is not verified. The verification status is reset to False whenever a field is set.",
)
def _update_verified_field(self):
"""
To be called in inheriting model's save() method.
"""
if self.is_dirty():
if not 'verified' in self.get_dirty_fields():
self.verified = False
class Meta:
abstract = True
class ModelToBeVerified(VerifiedModel):
...
def save(self, *args, **kwargs):
...
self._update_verified_field()
return super(ModelToBeVerified, self).save(*args, **kwargs)

generate django 2.0 model from a dict

Consider the following Django model:
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
I would like to define this class from a “static” dict (to be specific in my case it is generated from a file which will not change - so no need to makemigrations and migrate apart from first time).
Here is some pseudo-code to better explain what I want to achieve:
persondict = {‘first_name‘: models.CharField(max_length=255), ‘last_name’: models.CharField(max_length=255)}
class Person(models.Model):
#generate fields from persondict
This is just to start with. Note that you have to change the app_label inside the function, also this will work if you define it inside your models.py where you will use it, otherwise you will need to replace the '__module__' with the appropriate value.
def generate_person_model_from_a_dict(person_model_dict):
class Meta:
app_label = 'your_app_label_here'
person_model_dict.update({
'Meta': Meta,
'__module__': generate_person_model_from_a_dict.__module__,
})
Person = type("Person", (models.Model,), person_model_dict)
return Person
Meta.app_label is needed here to let the Django know which app the newly constructed model should be attached to, e.g. if you are creating it for the app blog and set the model's app_label to blog, then the Django will know, that the app has this model on it (just like you would define it in your blog/models.py)
__module__ is a special attribute of python objects (you can read details about it here https://docs.python.org/3/reference/datamodel.html ) To be brief, it lets Django to know which module your class belongs to, it uses it mostly to display various messages to it's user, see here: https://github.com/django/django/search?utf8=%E2%9C%93&q=module&type=
(just set it to your models.py like in the above example is therefore what Django pretty much expects)

Related models update on model field change

What is the most proper way to trigger update of related models, when one of the fields of parent model is changed? I have this set of models:
class ActivityObject(models.Model):
is_deleted = models.BooleanField(default=False)
class ActivityJob(models.Model):
activity_object = models.ForeignKey(
ActivityObject,
related_name='activity_jobs',
)
is_deleted = models.BooleanField(default=False)
so if I set ActivityObject.is_deleted = True on some instance all I want is that all related instances of ActivityJob also changed field is_deleted to True. Thanks in advance.
Overriding save() will work:
class ActivityObject(models.Model):
is_deleted = models.BooleanField(default=False)
def save(self, *args, **kwargs):
super(ActivityObject, self).save(args, kwargs)
if self.is_deleted:
for job in self.activity_jobs:
job.is_deleted = True
job.save()
Just guessing here, but if the real purpose of this is to delete ActivityJobs when related ActivityObjects are deleted, then you can just go ahead and delete the ActivityObject. Django's default behavior will remove all the ActivityJobs connected to it.
If you want to perform some other action when deleting, use Django's pre_delete or post_delete signals, which will call a function you define before/after deleting objects of the type you specify.
EDIT: if you ever use update() on querysets dealing with ActivityObject and changing is_deleted, you can either ensure that you perform a corresponding update() on ActivityJob, or you can override ActivityObject's queryset functionality like this to make it happen automatically.
You can use Django signals' pre_delete or post_delete. More details and examples are available in the Django Signals documentation.

Django south: access model's __unicode__() method

In django south data migration file, how can I access model's __unicode__() method?
Here is a simple example:
I have a Person class in models.py:
class Person(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
I then create a data migration with
python manage.py datamigration myapp check_person
In the data migration file, I have
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
for person in orm.Person.objects.all():
print person
Instead of printing the person's name, it prints
Person object
Person object
...
when I do:
from myapp.models import Person
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
print hasattr(orm.Person, '__unicode__')
print hasattr(Person, '__unicode__')
It prints
False
True
My question is that are those expected? Why can't south orm access __unicode__() method? How can I access it?
Edit:
Doing from myapp.models import Person and access Person objects directly by Person.objects.all() does not work for me, because the Person model will be removed from models.py in the next migration.
Instead of using orm.Person.objects.all(), you might want to import the original Person model and query on that instead. orm.Person is not the same thing as Person, it sort of depends on the big data structure models that defined at the end of each migration file to figure out the model information.
However, orm.Model would still work if your original model has been changed, that's why south could work with models that have changes, the model variable takes a snapshot of the models.py at the time it is created.
South doc explains this in details.
South doesn’t freeze every aspect of a model; for example, it doesn’t
preserve new managers, or custom model methods, as these would require
serialising the python code that runs those method (and the code that
depends on, and so forth).
Edit:
Sounds like OP would like to use the model __unicode__ property for certain purposes but the model would be deleted right after. Since an import error would occur in this case, I wouldn't bother using __unicode__ anymore, instead I would suggest handling this by manually accessing model fields using orm.Model(since __unicode__ is composed by field values anyway).

Categories