How to avoid model edit in presave Django - python

I am trying to save in Django model at once this would trigger an event. When updating this model triggering the start_event again and again. How to trigger the event only at first time save not in update?
Image(models.Model):
file_name = models.CharField(max_length=200)
file_path = models.CharFiedld(max_length=500)
def save(self, *args, **kwargs):
start_event(self.file_path)
super(Image, self).save(*args, **kwargs)
Using this model first create and update. Create
from models import Image
image = Image()
image.file_path = "path/to/file"
image.save()
After the event completion will use Update
from models import Image
image = Image.objects.get(file_path='path/to/file')
image.file_name = "file/name"
image.save()
How to trigger the event(start_event) only at first time save not in update?

#Zev your info helped it! From the link Django - Overriding the Model.create() method? found the solution.
def save(self, *args, **kwargs):
if not self.pk: # Checking if the oject has no pk
start_event(self.file_path)
super(Image, self).save(*args, **kwargs)

Related

Optimize Django model save method for accessing to uploaded file path

in my Django application models I need to access to an ImageField, in the save() method, to extract some gps information and populate the other MyModel fields before I save the entire model instance.
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
if self.image:
# Todo: remove the double call of super(Photo, self).save() method by accessing the file before it will save
super(Photo, self).save(force_insert, force_update, using, update_fields)
image_file = gpsimage.open(self.image.path) #gpsimage.open needs a file path that I can have it only after I call super(Photo, self).save(....)
...
...
...
# Final save call
super(Photo, self).save(force_insert, force_update, using, update_fields)
Is possible to access to a temp file (image) path, extract information from it, and than, if all fields are valid, save the entire model?
Any other elegant solution?
My current app is a REST and admin app, so I wish to add this logic in a centric model.
You can access the filepath from your custom save method like so: self.myimg.name, where myimg is the name of your field.
from django.db import models
import uuid
import os
def img_file_path(instance, filename):
''' Creates a filepath and name for an image file. '''
ext = filename.split('.')[-1]
filename = "%s.%s" % (uuid.uuid4(), ext)
today = datetime.date.today()
return os.path.join('imgs', '%s/%s/%s' % (today.year, today.month, today.day), filename)
class MyModel(models.Model):
myimg = models.ImageField(upload_to = img_file_path)
def save(self, *args, **kwargs):
path_of_file = self.myimg.name
# edit path_of_file, or whatever
self.myimg.name = path_of_file.upper()
super(MyModel, self).save()
If you take a look at the source code, the filename is determined in the __init__() method, so it'll be accessible to you as soon as the object is created (before being saved and committed).
I had the same problem and my solution was to save two times. Here's my code:
26 def save(self, *args, **kwargs):
27 super(GeoFile, self).save(*args, **kwargs)
28 self.filetype = self.possible_type()
29 if self.filetype == 'SHPZ':
30 self.fileinfo = self.shpz_info()
31 if 'srid' in self.fileinfo and not self.fileinfo['srid'] is None:
32 self.srid = self.fileinfo['srid']
41 super(GeoFile, self).save()

Django Save method needs to update model instance twice

Django Save method needs to update model instance twice for it to work.
An application has the following save method to generate unique ids based on the ID.
def save(self, *args, **kwargs):
if self.id and not self.fid:
self.fid = encode(self.id)
In reality it only works if model instance is saved twice, the first FID is None.
def save(self, *args, **kwargs):
# save the instance, do this first so that we have an id
super(MyModelClass, self).save(*args, **kwargs)
# if it was a new instance fid is not set yet
if self.id and not self.fid:
self.fid = encode(self.id)
# save the instance again now that we've set fid
super(MyModelClass, self).save(*args, **kwargs)

How to save a changed value in Django admin on api creation?

In Django Admin I am displaying a url.
This url is created using the id the object that it is attached to.
I'm using python, django and django-rest-framework.
In my views I have logic on the ApiDetail class. Here I override the 'get' method.
I increment the current object in views.py:
currentObject = Api.objects.get(id=pk)
currentObject.currentNumber += 1
currentObject.save()
return self.retrieve(request, *args, **kwargs)
In models.py I set the url field:
class Api(models.Model):
myUrl = models.CharField(max_length=500, blank=True, verbose_name="Url", editable=False)
def save(self, *args, **kwargs):
self.formUrl = "https://custumUrl/"+str(self.id)+"/"
super(Api, self).save(*args, **kwargs)
Here I override the api save method to update the formUrl field.
The problem I have is when a form is first added to Django admin and saved the url says:
https://custumUrl/none/
It should say:
https://custumUrl/1/
Or any number, but definitely the number of the objects id.
I think Daniel is right in their comments and you should follow their advice.
But if you don't want to do that, then you should first save an object, then assign an id value to the url, then save it again:
class Api(models.Model):
myUrl = models.CharField(max_length=500, blank=True, verbose_name="Url", editable=False)
def save(self, *args, **kwargs):
super(Api, self).save(*args, **kwargs)
self.formUrl = "https://custumUrl/"+str(self.id)+"/"
super(Api, self).save(*args, **kwargs)
Is currentNumber defined in he Api class?
Also, in your Api class, you have myUrl defined, but in the save method it's formUrl.
Maybe try something like this:
class Api(models.Model):
formUrl = models.CharField(max_length=500, blank=True, verbose_name="Url", editable=False)
def save(self):
"""If this is the firsts time populate required details, otherwise update it."""
if not self.id:
latest_obj = Api.latest('id')
this_id = latest_obj.id
self.formUrl = "https://custumUrl/"+str(this_id)+"/"
super(Api, self).save()
else:
#Save it as is
super(Api, self).save()

Get the django user in save while using django.form

I have a problem getting the user in django when I use django forms. My code looks something like this.
The view:
#login_required
def something(request):
item = ItemForm(request.POST)
item.save(user=request.user)
The form:
class ItemForm(ModelForm):
class Meta:
model = Item
fields = '__all__'
def save(self, *args, **kwargs):
user = kwargs['user']
super(ItemForm, self).save(user=user)
The model
class Item(models.Model):
field = models.CharField(max_length=100,)
field2 = models.CharField(max_length=100,)
def check_permissions(self, user):
return user.groups.filter(name='group').exists()
def save(self, *args, **kwargs):
if self.check_permissions(kwargs['user']):
super(Item, self).save()
My problem is that when I call the default save in ItemForm I get an error because the user param is unexpected. I need the user in the model to make the permission check but I dont know how to get it.
I finally solved the problem. The way I found was to save the form without the user but with the commit flag set to False and then calling the function save from the model with the user param.
The form save method now looks like this
def save(self, *args, **kwargs):
item = super(ItemForm, self).save(commit=False)
item.save(user=kwargs['user'])

Django. Override save for model

Before saving model I'm re-size a picture. But how can I check if new picture added or just description updated, so I can skip rescaling every time the model is saved?
class Model(model.Model):
image=models.ImageField(upload_to='folder')
thumb=models.ImageField(upload_to='folder')
description=models.CharField()
def save(self, *args, **kwargs):
if self.image:
small=rescale_image(self.image,width=100,height=100)
self.image_small=SimpleUploadedFile(name,small_pic)
super(Model, self).save(*args, **kwargs)
I want to rescale only if new image loaded or image updated, but not when description updated.
Some thoughts:
class Model(model.Model):
_image=models.ImageField(upload_to='folder')
thumb=models.ImageField(upload_to='folder')
description=models.CharField()
def set_image(self, val):
self._image = val
self._image_changed = True
# Or put whole logic in here
small = rescale_image(self.image,width=100,height=100)
self.image_small=SimpleUploadedFile(name,small_pic)
def get_image(self):
return self._image
image = property(get_image, set_image)
# this is not needed if small_image is created at set_image
def save(self, *args, **kwargs):
if getattr(self, '_image_changed', True):
small=rescale_image(self.image,width=100,height=100)
self.image_small=SimpleUploadedFile(name,small_pic)
super(Model, self).save(*args, **kwargs)
Not sure if it would play nice with all pseudo-auto django tools (Example: ModelForm, contrib.admin etc).
Check the model's pk field. If it is None, then it is a new object.
class Model(model.Model):
image=models.ImageField(upload_to='folder')
thumb=models.ImageField(upload_to='folder')
description=models.CharField()
def save(self, *args, **kwargs):
if 'form' in kwargs:
form=kwargs['form']
else:
form=None
if self.pk is None and form is not None and 'image' in form.changed_data:
small=rescale_image(self.image,width=100,height=100)
self.image_small=SimpleUploadedFile(name,small_pic)
super(Model, self).save(*args, **kwargs)
Edit: I've added a check for 'image' in form.changed_data. This assumes that you're using the admin site to update your images. You'll also have to override the default save_model method as indicated below.
class ModelAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.save(form=form)
You may supply extra argument for confirming a new image is posted.
Something like:
def save(self, new_image=False, *args, **kwargs):
if new_image:
small=rescale_image(self.image,width=100,height=100)
self.image_small=SimpleUploadedFile(name,small_pic)
super(Model, self).save(*args, **kwargs)
or pass request variable
def save(self, request=False, *args, **kwargs):
if request and request.FILES.get('image',False):
small=rescale_image(self.image,width=100,height=100)
self.image_small=SimpleUploadedFile(name,small_pic)
super(Model, self).save(*args, **kwargs)
I think these wont break your save when called simply.
You may put this in your admin.py so that this work with admin site too (for second of above solutions):
class ModelAdmin(admin.ModelAdmin):
....
def save_model(self, request, obj, form, change):
instance = form.save(commit=False)
instance.save(request=request)
return instance
What I did to achieve the goal was to make this..
# I added an extra_command argument that defaults to blank
def save(self, extra_command="", *args, **kwargs):
and below the save() method is this..
# override the save method to create an image thumbnail
if self.image and extra_command != "skip creating photo thumbnail":
# your logic here
so when i edit some fields but not editing the image, I put this..
Model.save("skip creating photo thumbnail")
you can replace the "skip creating photo thumbnail" with "im just editing the description" or a more formal text.
Hope this one helps!
Query the database for an existing record with the same PK. Compare the file sizes and checksums of the new and existing images to see if they're the same.
In new version it is like this:
def validate(self, attrs):
has_unknown_fields = set(self.initial_data) - set(self.fields.keys())
if has_unknown_fields:
raise serializers.ValidationError("Do not send extra fields")
return attrs
I have found one another simple way to store the data into the database
models.py
class LinkModel(models.Model):
link = models.CharField(max_length=500)
shortLink = models.CharField(max_length=30,unique=True)
In database I have only 2 variables
views.py
class HomeView(TemplateView):
def post(self,request, *args, **kwargs):
form = LinkForm(request.POST)
if form.is_valid():
text = form.cleaned_data['link'] # text for link
dbobj = LinkModel()
dbobj.link = text
self.no = self.gen.generateShortLink() # no for shortLink
dbobj.shortLink = str(self.no)
dbobj.save() # Saving from views.py
In this I have created the instance of model in views.py only and putting/saving data into 2 variables from views only.

Resources