Django: accessing session variable while overriding model's save method - django

Is there any way to access sessions variables while overriding any models save method
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
//Code for accessing session variable
super(Blog, self).save(*args, **kwargs)
Thanks,

not directly
you could add an extra argument to the save method and pop it off before calling the super save:
def save(self, *args, **kwargs):
request = kwargs.pop('request')
view...:
instance.save(request=request)
But
if you are saving a form it may be better to use
view...:
instance = form.save(commit=False)
# do some logic
instance.save()

Related

Conflict in form save() method when inherit from other save() method in Django

I changed the save method in the Django form.Then I inherited another save method from this method and made some changes to the child method ,that conflicted. I can't figure out how to fix the conflict so that my other uses of the parent method stay healthy and don't get spoiled.
Forms.py
class BaseModelForm(forms.ModelForm):
def save(self, commit=True, **kwargs):
"""
Save this form's self.instance object if commit=True. Otherwise, add
a save_m2m() method to the form which can be called after the instance
is saved manually at a later time. Return the model instance.
"""
if self.errors:
raise ValueError(
"The %s could not be %s because the data didn't validate." % (
self.instance._meta.object_name,
'created' if self.instance._state.adding else 'changed',
)
)
if commit:
# If committing, save the instance and the m2m data immediately.
self.instance.save(user=kwargs.pop('user'))
self._save_m2m()
else:
# If not committing, add a method to the form to allow deferred
# saving of m2m data.
self.save_m2m = self._save_m2m
return self.instance
class ChildForm(BaseModelForm):
def save(self, commit=True, **kwargs):
new_instance = super(ChildForm, self).save(commit=True)
# Some other codes goes here!
return new_instance
Models.py
class BaseFieldsModel(models.Model):
def save(self, *args, **kwargs):
user = kwargs.pop('user', None)
if user:
if self.pk is None:
self.created_by = user
self.updated_by = user
super(BaseFieldsModel, self).save(*args, **kwargs)
Views.py
def my_view(request,id):
if form.is_valid():
instance = form.save(commit=False)
# Some codes goes here!
instance.save(user=request.user)
And error is:
KeyError at /my/url
Request Method: POST
'user'
Exception Type: KeyError
Exception Value:
'user'
And Django Debug page separately highlight these three lines:
instance = form.save(commit=False)
new_instance = super(ChildForm, self).save(commit=True)
self.instance.save(user=kwargs.pop('user'))
You're trying to get user in BaseModelForm.save(), but you never passed the user to the form.save() calls. You need to add form.save(..., user=request.user):
def my_view(request,id):
...
instance = form.save(commit=False, user=request.user)
and also pass it along in super(ChildForm, self).save(..., **kwargs)
class ChildForm(BaseModelForm):
def save(self, commit=True, **kwargs):
new_instance = super(ChildForm, self).save(commit=True, **kwargs)
...
Also, you probably want to pass super(ChildForm, self).save(commit=commit, ...) in ChildForm:
new_instance = super(ChildForm, self).save(commit=True, **kwargs)
because otherwise the form class may not respect the commit flag being passed from the view (unless of course, you've already handled this in your elided code).

Add a field value outside form in Django

Whenever I have to add a value to the instance of a form obtained from the context or from the URL I do it in the following way, using form.instance.
class PreguntaForm(forms.ModelForm):
class Meta:
model = Pregunta
fields = ('etiqueta', 'grupo', 'tipo_pregunta', 'opciones', 'mostrar_tabla', 'activo')
def __init__(self, *args, **kwargs):
cuestionario = kwargs.pop('cuestionario', False)
super(PreguntaForm, self).__init__(*args, **kwargs)
self.fields['grupo'].queryset = Grupo.objects.filter(cuestionario=cuestionario)
class PreguntaNueva(InfoPregunta, CreateView):
form_class = PreguntaForm
encabezado = 'Nueva Pregunta'
model = Pregunta
def get_form_kwargs(self):
kwargs = super(PreguntaNueva, self).get_form_kwargs()
kwargs['cuestionario'] = self.dame_cuestionario()
return kwargs
def form_valid(self, form):
form.instance.cuestionario = self.dame_cuestionario()
return super(PreguntaNueva, self).form_valid(form)
The problem that arises now is that I want to perform a check CreateView and EditView. To DRY, I want to do it in the clean method of the model, but the value that I assign to form.instance.cuestionario, is not available within the clean method. How could I do it? This value must not be edited by the user in any case.
Yes it is, you pass it in via get_form_kwargs; you just need to assign it to an instance variable in the form's __init__.
def __init__(self, *args, **kwargs):
self.cuestionario = kwargs.pop('cuestionario', False)
super(PreguntaForm, self).__init__(*args, **kwargs)
self.fields['grupo'].queryset = Grupo.objects.filter(cuestionario=self.cuestionario)
def clean(self):
# do something with self.cuestionario

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)

ModelForm User Mixin

I've got some models with user field.
For this purpose I'd like to create a form mixin that would add self.user instance (which is provided to the form in views). Is it possible ?
Here's the example
class UserFormMixin(object):
"""Removes user instance from kwargs and adding it to object"""
def __init__(self, *args, **kwargs):
super(UserFormMixin, self).__init__(*args, **kwargs)
self.user = kwargs.pop('user')
def save(self, **kwargs):
obj = super(UserFormMixin, self).save(commit=False)
obj.user = self.user
if kwargs['commit']:
return obj.save()
else:
return obj
What I'd like to achieve:
class SomeFormWithUserField(UserFormMixin, ModelForm):
class Meta:
model = SomeModelWithUserField
fields = ['fields without user']
def save(self, **kwargs):
data = super(SomeFormWithUserField, sefl).save(commit=False)
#data already with user prepended
#do some other stuff with data
if kwargs['commit']:
return data.save()
else
return data
class SomeOtherFormWithUser(UserFormMixin, ModelForm):
class Meta:
model = SomeOtherModel
fields = ['some fields without user']
# no need to save here.. standard model form with user prepended on save()
The problem is that UserFormMixin doesn't know about model instance? Or am I wrong here?
I am getting some problems.. like 'commit' kwargs key error.. or object is not saved..
You're close, you just have some logic errors. First, in order to override ModelForm methods, your mixin needs to inherit from ModelForm.
class UserFormMixin(forms.ModelForm):
...
Then, any forms that inherit from it just inherit UserFormMixin, not ModelForm.
class SomeOtherFormWithUser(UserFormMixin):
...
Second, your __init__ method override is incorrect. You need to accept any and all args and kwargs that get passed into it.
def __init__(self, *args, **kwargs):
...
Finally, don't override the save method again, in the subclass. I guess it won't technically hurt anything, but what's the point of inheritance if you're going to repeat code, anyways? If user is not nullable, you can always add an if block to check if self.user is not None before adding it to the model. Of course, if user is not nullable, your model won't likely save without self.user anyways.
This one seems to work fine. Thanks Chris!
If this can be coded better please let me know.
class UserFormMixin(forms.ModelForm):
"""Removes user instance from kwargs and adding it to object"""
def __init__(self, *args, **kwargs):
super(UserFormMixin, self).__init__(*args, **kwargs)
self.user = kwargs.pop('user')
def save(self, commit=True):
obj = super(UserFormMixin, self).save(commit=False)
obj.user = self.user
if commit:
return obj.save()
else:
return obj
class SomeFormWithUserField(UserFormMixin):
class Meta:
model = SomeModelWithUserField
fields = ['fields without user']
def save(self, **kwargs):
data = super(SomeFormWithUserField, sefl).save(commit=False)
#data already with user prepended
#do some other stuff with data
# self.send_mail() f.e.
return data.save()
class SomeOtherFormWithUser(UserFormMixin):
class Meta:
model = SomeOtherModel
fields = ['some fields without user']
# this will work too

Django snippets. How to use?

explain me please how to use it in my Admin?
You can just create a custom ModelForm for your model, with the following:
remove_the_file = forms.BooleanField(required=False)
def save(self, *args, **kwargs):
object = super(self.__class__, self).save(*args, **kwargs)
if self.cleaned_data.get('remove_the_file'):
object.the_file = ''
return object
Use that form in your ModelAdmin, and there's no need to change the database.
there is what i created in forms.py:
class MediaForm(forms.ModelForm):
remove_the_file = forms.BooleanField(required=False)
def save(self, *args, **kwargs):
object = super(self.__class__, self).save(*args, **kwargs)
if self.cleaned_data.get('remove_the_file'):
object.the_file = ''
return object
And there is my admin.py:
class MediaAdmin(admin.ModelAdmin):
raw_id_fields = ('parent',)
how should i change MediaAdmin class to apply it?
class MediaAdmin(admin.ModelAdmin):
raw_id_fields = ('parent',)
form = MediaForm