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

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.

Related

Django DetailView but get_object_or_404() instead of get_object()

I have Django Model that has a live boolean attribute.
I want the View to get the Model by slug and only go to this page if it's live USING THE DetailView (not function based view, because I want to see how to do it.
Model definition
# myapp/models.py
class MyModel(models.Model):
name = models.CharField(max_length=255)
live = models.BooleanField(default=True)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
I hoped it would be done something like this:
class ModelDetailView(DetailView):
model = MyModel
def get(self, request, *args, **kwargs):
service = self.get_object_or_404(Service, live=True) # <- Main point of what I'm looking for help with
return super().get(request, *args, *kwargs)
Is there a way to filter this way?
You can specify the queryset to filter, so:
class ModelDetailView(DetailView):
model = MyModel
queryset = MyModel.objects.filter(live=True)
You thus do not need to implement the .get(…) method at all.

Trying to do a calculation on django models

i have two integer fields that i want to divide to get the value of 3rd field.
#property
def Pallets_Count(self):
return self.CASES/self.CasesPerPallet
but the result in the database always shows null .
#property
def Pallets_Count(self):
return self.CASES/self.CasesPerPallet
#property will not save anything into your model field. It works like a method. You can call in in your template like a model field mypost.Pallets_Count.
If you want to put the result into a database field, you need to override save method. But this might not be necessary. Property most likely is enough.
class MyModel(models.Model):
# your other fields
fieldname = models.FloatField()
def save(self, *args, **kwargs):
self.fieldname = self.CASES/self.CasesPerPallet
super(MyModel, self).save(*args, **kwargs)

Python Populating ManyToManyField automativally using same model instance

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()

Does a model's save() method get called when using loaddata for fixtures?

I'm trying to generate an automatic slug for a model whenever it is empty, from another field. This is the code:
class Position(RichText):
name = models.CharField(max_length=200)
slug = models.SlugField(null=True)
def position_description(self):
return self.content
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super(Position, self).save(*args, **kwargs)
When I load initial fixtures with loaddata, it seems the save() method is never triggered. Is this normal behavior? How can I catch fixtures too?
This is normal behavior, from the documentation:
When fixture files are processed, the data is saved to the database as
is. Model defined save methods and pre_save signals are not called
.

Django admin - remove field if editing an object

I have a model which is accessible through the Django admin area, something like the following:
# model
class Foo(models.Model):
field_a = models.CharField(max_length=100)
field_b = models.CharField(max_length=100)
# admin.py
class FooAdmin(admin.ModelAdmin):
pass
Let's say that I want to show field_a and field_b if the user is adding an object, but only field_a if the user is editing an object. Is there a simple way to do this, perhaps using the fields attribute?
If if comes to it, I could hack a JavaScript solution, but it doesn't feel right to do that at all!
You can create a custom ModelForm for the admin to drop the field in the __init__
class FooForm(forms.ModelForm):
class Meta(object):
model = Foo
def __init__(self, *args, **kwargs):
super(FooForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
# Since the pk is set this is not a new instance
del self.fields['field_b']
class FooAdmin(admin.ModelAdmin):
form = FooForm
EDIT: Taking a hint from John's comment about making the field read-only, you could make this a hidden field and override the clean to ensure the value doesn't change.
class FooForm(forms.ModelForm):
class Meta(object):
model = Foo
def __init__(self, *args, **kwargs):
super(FooForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
# Since the pk is set this is not a new instance
self.fields['field_b'].widget = forms.HiddenInput()
def clean_field_b(self):
if self.instance and self.instance.pk:
return self.instance.field_b
else:
return self.cleaned_data['field_b']
You can also do the following
class FooAdmin(admin.ModelAdmin)
def change_view(self, request, object_id, extra_context=None):
self.exclude = ('field_b', )
return super(SubSectionAdmin, self).change_view(request, object_id, extra_context)
Taken from here Django admin: exclude field on change form only