I need to get every class, that inherits models.Model to have created and updated field. I can achieve this by adding custom save method to every field,
def save(self, *args, **kwargs):
if not self.id:
self.created = timezone.now()
self.modified = timezone.now()
return super(`models.Model`, self).save(*args, **kwargs)
but this violates Don'tRepeatYourself rule.
I've tried to override models.Model:
class LogModel(models.Model):
created = models.DateTimeField(editable=False)
updated = models.DateTimeField()
def save(self, *args, **kwargs):
if not self.id:
self.created = timezone.now()
self.updated = timezone.now()
return super(LogModel, self).save(*args, **kwargs)
and use LogModel instead of models.Model, but this failed with error E006(
The field 'x' clashes with the field 'x' from model 'y.logmodel'.
EDIT
My main question is how to add a custom specific field to all models in my models.py
Your base model must be abstract:
class LogModel(models.Model):
class Meta:
abstract = True
created = models.DateTimeField(editable=False)
updated = models.DateTimeField()
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
# Use self._state.adding to check if this is a new instance,
# ID not being empty is not a guarantee that the instance
# exists in the database
# and if `update_fields` is passed, you must add the fields to the
# list or they won't be saved in the database.
if force_insert or self._state.adding:
self.created = timezone.now()
if update_fields and 'created' not in update_fields:
update_fields.append('created')
self.updated = timezone.now()
if update_fields and 'updated' not in update_fields:
update_fields.append('updated')
return super(LogModel, self).save(*args, **kwargs)
However, if you override the save() method, this means it won't be editable in any form. If this is what you want, then you are better to use auto_now and auto_now_add:
class LogModel(models.Model):
class Meta:
abstract = True
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
Instead of overriding save method, you could define auto_now_add and auto_now parameters in the Model field like:
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
For more information on these parameters, you can check the django docs.
It can be done by defining Abstract Base Model and define save method there and create all the models by inheriting from the abstract class. e.g.
class MyAbstractModel(models.Model):
created = models.DateTimeField(editable=False)
updated = models.DateTimeField()
def save(self, *args, **kwargs):
if self._state.adding:
self.created = timezone.now()
self.updated = timezone.now()
return super(LogModel, self).save(*args, **kwargs)
class Meta:
abstract = True
and create child model class from it:
class Record(MyAbstractModel):
pass
Unfortunately, using update_fields in .save() does not trigger fields with auto_now, so I overridden save method this way:
class BaseModel(models.Model):
class Meta:
abstract = True
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
_base_update_fields = ['updated_at']
def save(
self, force_insert=False, force_update=False,
using=None, update_fields=None
):
_update_fields = None
if update_fields is not None:
_update_fields = self._base_update_fields + update_fields
super().save(
force_insert=force_insert, force_update=force_update,
using=using, update_fields=_update_fields
)
Related
I have a problem when I try to update a field named date_updated. My intention with the field is that every time a record is updated, the date_updated field of that record should be updated by the date the change is made. That field and one other field I have inside a Base class and then in each of the models I inherit that class to repeat the fields.
class Base(models.Model):
...
date_updated = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
self.date_updated = django.timezone.now()
super(Base, self).save(*args, **kwargs)
class Meta:
abstract = True
class OtherClass(Base):
...
My intention is that when I update any record in the OtherClass, its date_updated field will be updated.
I also tried adding the overwrite of the save method in the OtherClass, but the result is the same. The date is kept after I make the change.
I am making the change with .update(**data_to_update)
I did this when i wanted to update only the updated_on (datetime) column:
This might help you:
from datetime import datetime
def get_current_datetime_str():
now = datetime.now()
return now.strftime("%Y-%m-%d %H:%M:%S")
class ModelName(models.Model):
date_updated=models.CharField(max_length=100) #whatever your field is
...
def __str__(self):
return self.name
continue and write this below def str(self):
def save(self, *args, **kwargs):
self.date_updated = get_current_datetime_str()
super().save(*args, **kwargs)
Hello I have a function to auto generate data for my SlugField but i dont know how to implement a save method to execute it. Ive tried calling the function from the save method but it raises an error of 'instance not defined'. Any suggestions will help. Thanks.
def ran_str_gen(size=6, chars=string.ascii_letters + string.digits):
return ''.join(secrets.choice(chars) for s in range(size))
def slug_gen(instance, new_slug=None):
if new_slug is not None:
slug=new_slug
else:
slug = slugify(instance.title)
op = instance.__class__
qs_exists = op.objects.filter(slug=slug).exists()
if not qs_exists:
new_slug = '{slug}-{ranstr}'.format(slug=slug, ranstr=ran_str_gen())
return slug_gen(instance, new_slug=new_slug)
return slug
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.FloatField()
slug = models.SlugField()
def save(self, *args, **kwargs):
slug_gen()
You should pass the instance (self) to the slug_gen function, store the result in the slug field, and then make a super() call to save the model effectively, so:
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.FloatField()
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slug_gen(self)
super().save(*args, **kwargs)
Note: You can make use of django-autoslugĀ [GitHub] to automatically create a slug based on other field(s).
Note: Normally you should not change slugs when the related fields change. As is written in the article Cool URIs don't changeĀ [w3.org], URIs are not supposed to change, since these can be bookmarked. Therefore the slug should only be created when creating the object, not when you change any field on which the slug depends.
def save(self, *args, **kwargs):
self.slug=slug_gen()
I am trying to joint two models in django-rest-framework.
My code isn't throwing any error but also it isn't showing other model fields that need to be joined.
Below is my code snippet:
Serializer:
class CompaniesSerializer(serializers.ModelSerializer):
class Meta:
model = Companies
fields = ('id', 'title', 'category')
class JobhistorySerializer(serializers.ModelSerializer):
companies = CompaniesSerializer(many=True,read_only=True)
class Meta:
model = Jobhistory
fields = ('id', 'title', 'company_id', 'companies')
View .
class UserJobs(generics.ListAPIView):
serializer_class = JobhistorySerializer()
def get_queryset(self):
user_id = self.kwargs['user_id']
data = Jobhistory.objects.filter(user_id=user_id)
return data
model:
class Companies(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=100, blank=True, default='')
category = models.CharField(max_length=30, blank=True, default='')
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
def save(self, *args, **kwargs):
title = self.title or False
category = self.category or False
super(Companies, self).save(*args, **kwargs)
class Jobhistory(models.Model):
id = models.AutoField(primary_key=True)
company_id = models.ForeignKey(Companies)
title = models.CharField(max_length=100, blank=True, default='')
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
def save(self, *args, **kwargs):
company_id = self.company_id or False
title = self.title or False
super(Jobhistory, self).save(*args, **kwargs)
Thanks in advance. Any help will be appreciated.
In your views, you have
serializer_class = JobHistorySerializer()
Remove the parenthesis from this.
The reason for this is apparent in the GenericAPIView, specifically the get_serializer() and get_serializer_class() methods:
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.
You may want to override this if you need to provide different
serializations depending on the incoming request.
(Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
As you can see in get_serializer, it initializes that serializer class with args and kwargs that aren't provided in your view code.
So basically I am trying to override the save method of a model to tell if a certain non-required field, which is a m2m field, is specified. If so, then update one of its own Boolean field to True. Currently I have something like this:
class Flag(models.Model):
name = models.CharField(max_length=300)
def __str__(self):
return self.name
class Model(models.Model):
BooleanField = models.BooleanField(default = False)
m2mField = models.ManyToManyField(Flag)
def save(self, *args, **kwargs):
super(Model, self).save(*args, **kwargs)
if Model.objects.filter(id = self.id, m2mField = None).exists():
Model.objects.filter(id = self.id).update(BooleanField = True)
And this is not working for me now. I don't really care what is in the m2m field, just trying to know if that field is specified by user when creating this instance.
TIA
Edit: I update with a more realistic definition.
You could try checking if the m2mField count is more than 0, like so:
class Model(models.Model):
BooleanField = models.BooleanField(default = False)
m2mField = models.ManyToManyField(Flag, blank=True)
def save(self, *args, **kwargs):
self.BooleanField = True if self.pk is not None and self.m2mField.count() > 0 else False
super(Model, self).save(*args, **kwargs)
It would probably a good idea to pass the blank=True argument to the m2mField.
I want to be able to add fields to django admin form at runtime. My model and form:
#admin.py
class SitesForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SitesForm, self).__init__(*args, **kwargs)
self.fields['mynewfield'] = forms.CharField()
class SitesAdmin(admin.ModelAdmin):
form = SitesForm
admin.site.register(Sites,SitesAdmin)
#model.py
class Sites(models.Model):
url = models.URLField(u'URL')
is_active = models.BooleanField(default=True, blank=True)
is_new = models.BooleanField(default=False, blank=True)
group = models.ForeignKey('SitesGroup')
config = models.TextField(blank=True)
Field mynewfield isn't displayed in form. Why?
You shouldn't be adding a new field to your form in that way, you can just do it as you would any other field and the form will contain both the Model's original fields and your new fields:
class SitesForm(forms.ModelForm):
mynewfield = forms.CharField(max_length=255, blank=True)
class Meta:
model = Sites
class SitesAdmin(admin.ModelAdmin):
form = SitesForm
admin.site.register(Sites, SitesAdmin)
Edit: Sorry, should have read what you had written a little better. If you want a dynamic field like that, then you need to do the following and it will do exactly what you want:
class SitesForm(forms.ModelForm):
class Meta:
model = Sites
def __init__(self, *args, **kwargs):
self.base_fields['mynewfield'] = forms.CharField(max_length=255, blank=True)
super(SitesForm, self).__init__(*args, **kwargs)
class SitesAdmin(admin.ModelAdmin):
form = SitesForm
admin.site.register(Sites, SitesAdmin)
It's the base_fields that gets composed by the metaclass that holds the fields that the form will use.
Solution:
class AdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AdminForm, self).__init__(*args, **kwargs)
self.fields.insert(1, 'myfield', forms.CharField())
class MyAdmin(admin.ModelAdmin):
form = AdminForm
def get_fieldsets(self, request, obj=None):
return (
(None, {
'fields': (..., 'myfield',),
}),
)