Python Populating ManyToManyField automativally using same model instance - django

I am screwed up to solve a problem for the last 7 days, yet I couldn't solve this problem! so much frustrated now!!!
I want when I create new GroupMess with an admin, the same admin should automatically add to members field,
this is my models:
class GroupMess(models.Model):
admin = models.OneToOneField(
User,
on_delete=models.CASCADE,
related_name='mess_admin'
)
members = models.ManyToManyField(User, related_name='mess_members', blank=True)
def save(self, *args, **kwargs):
self.members.add(self.admin)
super(GroupMess, self).save(*args, *kwargs)
If I try to create new GroupMess, it throws me this error:
"<GroupMess: GroupMess object (None)>" needs to have a value for field "id" before this many-to-many relationship can be used.
If i override save method like this:
def save(self, *args, **kwargs):
super(GroupMess, self).save(*args, *kwargs)
self.members.add(self.admin)
then it doesn't throw any error but the problem is, the members field remains blank
Can anyone help to fix this?
I want when I create GroupMess, I will add the admin during creating groupmess and the members filed should be filled automatically with the admin
*I mean, A group admin also will be a group member *

After you add the members to the Group model, save it again.
def save(self, *args, **kwargs):
super(Group, self).save(*args, **kwargs)
self.members.add(self.admin)
self.save()

Related

Performing actions on a related model while saving

I need help. For several days now I have been unable to figure out how to get around one problem. I would appreciate any idea or advice.
For simplicity, there is a set of models:
class A(models.Model):
name = models.CharField(max_length=255)
class B(models.Model):
name = models.CharField(max_length=255)
owner = models.ForeignKey(A, on_delete=models.CASCADE, null=False)
def foo():
do something
The number of instances of B belonging to A is not known in advance. The number of instances of B is determined by the user at the time of creating a new instance of A. Moreover, he can create an instance of A without creating a single instance of B (empty set).
Challenge: I need to run foo after saving a new instance of A. This function should handle both the fields of the new instance of A and fields of B (back relation).
Yes, I have set the receiver to post_save signal model A. But here's the problem, at the moment when A is already saved (post_save), instances of B have not yet been saved (pre_save), and the values ​​of the corresponding fields have not been determined. In other words, I cannot get raw B values ​​in receiver A.
Any ideas? Am I doing something wrong?
I found a solution. Maybe it will be useful to someone.
I took advice from our more experienced colleagues to use signals only as a last resort. And the solution turned out to be simple. Although the solution may not seem very elegant.
The solution is based on overriding the save() method of each model
class A(models.Model):
name = models.CharField(max_length=255)
def _call_foo(self, *args, **kwargs):
do something
foo(*args, **kwargs)
def save(self, *args, **kwargs):
changed_fields = []
pk = None
if self.pk:
cls = self.__class__
old = cls.objects.get(pk=self.pk)
new = self
for field in cls._meta.get_fields():
field_name = field.name
try:
if getattr(old, field_name) != getattr(new, field_name):
pk = self.pk
changed_fields.append(field_name)
# Catch field does not exist exception
except Exception as ex:
print(type(ex))
print(ex.arg)
print(ex)
kwargs['update_fields'] = changed_fields
super().save(*args, **kwargs)
else:
super().save(*args, **kwargs)
pk = self.pk
changed_fields.append('__all__')
self._call_foo(self, pk, changed_fields)
class B(models.Model):
name = models.CharField(max_length=255)
owner = models.ForeignKey(A, on_delete=models.CASCADE, null=False)
def save(self, *args, **kwargs):
changed_fields = []
pk = None
if self.pk:
cls = self.__class__
old = cls.objects.get(pk=self.pk)
new = self
for field in cls._meta.get_fields():
field_name = field.name
try:
if getattr(old, field_name) != getattr(new, field_name):
pk = self.pk
changed_fields.append(field_name)
# Catch field does not exist exception
except Exception as ex:
print(type(ex))
print(ex.arg)
print(ex)
kwargs['update_fields'] = changed_fields
super().save(*args, **kwargs)
else:
super().save(*args, **kwargs)
pk = self.pk
changed_fields.append('__all__')
self.owner._call_foo(self, pk, changed_fields)
This solution is better than using signals, at least in that the save method is called if there is a real need for it: either an instance is created, or it is updated.
Of course, need to keep in mind that if the user updates or create several instances of both models (for example, in the inline form, the admin creates several instances of B and one instance of A), then the foo() function will be called several times. But this can already be worked out either at the _call_foo() method level, or by the logic of the foo() function itself.

How to auto fill SlugField instead of overriding save()?

Hello Awesome People
I wonder if there is a way to generate slug, rather than overriding the save() methods of my models.
Here's how I used to do:
def save(self, *args, **kwargs):
if self.pk is None:
self.slug = create_slug(self)
super(ModelName, self).save(*args, **kwargs)
create_slug is a function that generates the slug with slugify
def create_slug(instance,new_slug=None,field="name"):
''' long stuff to ensure the slug is unique '''
return slug
As django can do in Django Admin by automatically filling the slug field. Possible that we can simply add
an additional argument models.SlugField(field='name'). by adding this argument, django will make sure to take the value of field = name before saving to generate the slug without always overriding the save() method by copy/paste a lot of code to generate a slug for each model that We have.
I'm a little tired of copying this for all models, any hint will help!
Thank you in advance!
No, there is no such argument on model level. You can do it on admin level using prepopulated_fields:
class ArticleAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
On the model level you can write custom mixin class and use it as parent for all models:
class SlugMixin:
def create_slug(instance,new_slug=None,field="name"):
''' long stuff to ensure the slug is unique '''
return slug
def save(self, *args, **kwargs):
if self.pk is None:
self.slug = create_slug(self)
super().save(*args, **kwargs)
class SomeModel(SlugMixin, models.Model):
...
class OtherModel(SlugMixin, models.Model):
...
In this case you don't need to implement create_slug and override save in each models.

Before save model check some field

I need to check if other models already created, have a field filled .
If another model has the field with any value, the current model that attempts to create should not happen. And if possible send an error message.
This is my current code:
class Video(models.Model):
#####
# Fields of model
#####
def save(self, force_insert=False, force_update=False, *args, **kwargs):
some_video = Video.objects.all().filter(field_boolean=True).first()
if not some_video:
# Save current model
super(Video, self).save(force_insert, force_update, *args, **kwargs)
else:
# avoid save method for the current model created and send error message
What am I doing wrong or what I'm missing? What is the correct way to do this?
Firstly, you do not need to use all() and filter() together. Secondly, use exists() instead of first(). It returns True if the QuerySet contains any results, and False if not. This tries to perform the query in the simplest and fastest way possible.
class Video(models.Model):
name = models.CharField(max_length=30)
field_boolean = models.BooleanField()
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if Video.objects.filter(field_boolean=True).exists():
print('Video with field_boolean=True exists')
else:
super(Video, self).save(*args, **kwargs)

Prevent shell from saving attribute in Django

I have an attribute on a model that I don't want another developer to be able to go into the Django shell and change. Anyone know how to do this? I tried overwriting the save method on that model but I can't determine if that attribute has been changed.
Well I figured out how to accomplish this. Another developer could always change the code but this raises an error saying that's not what they're supposed to do.
class myModel(models.Model):
uuid = UUIDField('UUID', primary_key=True, default=uuid4)
model_type = models.ForeignKey(ModelType)
# override the Press model __init__ method to store initial press_type
def __init__(self, *args, **kwargs):
super(myModel, self).__init__(*args, **kwargs)
self.__model_type = self.model_type
# override the save method to prevent updates to press_type
def save(self, *args, **kwargs):
# raise an exception if press_type was changed since initialized
if self.pk and self.__model_type != self.model_type:
raise Exception('The model_type field cannot be changed once set.')
super(myModel, self).save(*args, **kwargs)

Django models: set value before saving

I have a model for which I would be able to set a value automatically before saving it to the database... so far I did override save() in this way:
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if not self.paramX:
self.paramX = value
super(Post, self).save(force_insert, force_update, using, update_fields)
Anyway, this does not work using bulk_create()... what can I do? (I'm trying using Signals but with no luck)
Extract from the documentation : (https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create)
The model’s save() method will not be called, and the pre_save and post_save signals will not be sent.
It is because the bulk saving method use an other way to save the data. And so a way is to manually do it as you create each entry to insert.
My preference would be the following:
class Instrument(models.Model):
name = models.CharField(max_length=20)
def __init__(self, *args, **kwargs):
super(Instrument, self).__init__(*args, **kwargs)
self.name = "Bass"
Edit:
I just re-read your question. If you're only concerned with whether or not the “paramX” field is set upon saving, couldn't you just add a “default” to the model field? i.e.
class Instrument(models.Model):
name = models.CharField(max_length=20, default="No instrument")