What *args, **kwargs doing in this save method overriding - django

I'm new to Django. I understand what are the usage of *args and **kwargs. And also know how to use them in method overriding.
But, I don't understand what purpose they serve while overriding the save() method in a model class.
My observation is that no number of arguments, either non-keyworded or keyworded, were assigned to them anywhere. Still why do I must use them and how.
Have this example:
class DemoModel(models.Model):
title = models.CharField(max_length = 200)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(DemoModel, self).save(*args, **kwargs)
Please explain.

From Django model documentation:
It’s also important that you pass through the arguments that can be
passed to the model method – that’s what the *args, **kwargs bit does.
Django will, from time to time, extend the capabilities of built-in
model methods, adding new arguments. If you use *args, **kwargs in
your method definitions, you are guaranteed that your code will
automatically support those arguments when they are added.

Very late but,
You should add return to the save method.
return super(DemoModel, self).save(*args, **kwargs)
Secondly, the kwargs which means keyword arguments are also URL parameters that are passed to the views like kwargs={"pk": self.object.id} when you save the method and it needs to redirect to a detail view for example, it needs the id of the just created object. The magic happens already in Django views, but you can pass extra parameters if you want.

Related

Passing kwargs into get_or_create method in Django

I have a model with a custom save function, where I want to perform certain functions based on a condition like this:
class ClassName(models.Model):
def save(self, *args, **kwargs):
reindex = **kwargs.pop("reindex")
super().save(*args, **kwargs)
if reindex:
People.objects.create()
Now inside a task I want to call the following:
kwargs = { "reindex": False}
ClassName.objects.get_or_create(**kwargs)
When it does a create, it obviously runs the save function, but it's giving me an error saying reindex is not a field.
I have been researching it for a while now and can't figure out what to do.
Maybe someone can point me in the right direction.
I just want to pass in an argument into the get_or_create, so that I can conditionally perform a certain function in the save method.
When you do
kwargs = { "reindex": False}
ClassName.objects.get_or_create(**kwargs)
it is actually equivalent to
ClassName.objects.get_or_create(reindex=False)
Thus, since reindex appears not to be a field defined in the model ClassName, you get an error.
BTW, beyond things which appear erroneous, e.g. reindex = **kwargs.pop("reindex"), you should define reindex as one of the fields of your model. But I admit that I answer blindly, because to me, your class definition cannot work like so. If one assumes that reindex is an integer field, you could do
class ClassName(models.Model):
reindex = models.IntegerField(null=True)
def save(self, *args, **kwargs):
super(ClassName, self).save(*args, **kwargs)
if "reindex" in kwargs:
People.objects.create()

Overriding a django model default value in the default form?

I have a model that looks something like:
class DooDad(models.Model):
doo_dad_dogue = models.BooleanField(default=True)
Trouble is, that default needs to be manipulated by... stuff that is irrelevant to this question. If I were creating the form that creates the object, the solution would be trivial. I'm still using the django default form for creating these things, though, and I'd rather keep it that way.
I tried the obvious:
class DooDad(models.Model):
doo_dad_dogue = models.BooleanField(default=True)
def __init__(self, *args, **kwargs):
super(DooDad, self).__init__(*args, **kwargs)
self.doo_dad_dogue = False
...which I suspect would have terrible side effects, but was worth experimenting with. The form still comes up with the box checked.
EDIT: I should have mentioned that this is Django 1.9
If it is not possible to continue using the default model creation form, is there anything unusual that I need to do to to make a ModelForm that only impacts CREATE, and not EDIT?
I do not think using the __init__in model is a good practice. However, if you want to try it is important to know that your code is not correct one the field doo_dad_dogue is a descriptor. The correct way to access it is
using self.fields['doo_dad_dogue'] = False.
Using a form is the correct way to do that. You can override the default value in the Form by using the init method:
def __init__(self, *args, **kwargs):
super(<YOUR_FORM>, self).__init__(*args, **kwargs)
if args is not None and len(args) and args[0] is not None:
<MANIPULATE HERE>
Hope that helps.

Django forms adding class attribute from the constructor

As you can see in the code sample below, I'm trying to add that multiple choice field from my constructor (instead of doing it like in the commented line) but it doesn't seem to work, doesn't matter if it's before or after the call of super().
Any advices on how i can add that attribute from my constructor?
class PageForm(forms.Form):
# answers = forms.ModelMultipleChoiceField(Answer.objects.all())
def __init__(self, *args, **kwargs):
self.answers = forms.ModelMultipleChoiceField(Answer.objects.all())
super(forms.Form, self).__init__(*args, **kwargs)
self.answers = forms.ModelMultipleChoiceField(Answer.objects.all())
P.S. I know it might be irrelevant for this example, but I need this thing for a more complex thing :D
Fields need to be added after super. Instead self.answers, try self.fields['answers']

Django: return several views from a single URL without redirection

With function based Django view it was simple to switch between several different views based on a condition, e.g. something like:
def base_view(request):
if some_condition():
return foo_view(request)
else:
return bar_view(request)
I can't find a simple way to do the same with the new class-based generic views. The only way I can think of is to redisrect, which I would like to avoid for various reasons:
def base_view(request):
if some_condition():
return redirect(reverse("name_of_url_to_class-based_view_foo"))
else:
return redirect("/url_to_class-based_view_bar/")
Any suggestions?
This is equivalent to your example with class based views.
class FooView(View):
pass
class BarView(View):
pass
class BaseView(View):
# staticmethod to avoid adding 'self' to the arguments
foo_view = staticmethod(FooView.as_view())
bar_view = staticmethod(BarView.as_view())
def dispatch(self, request, *args, **kwargs):
if some_condition():
return self.foo_view(request, *args, **kwargs)
else:
return self.bar_view(request, *args, **kwargs)
Even though the Django docs do say that the function based generic views are now deprecated I think the only reason to switch would be if you're writing less code.
If you're still set on switching, you'll want to first identify which class based views or mixins are most appropriate (single object, multiple objects, date based, forms, etc.). If the conditional was used to select a function that returns different context data / template to provide to the view, you can push the conditional down into an overridden get_queryset|get_context_data|get_object|get_template_names depending on your use case.
For example,
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(BaseView, self).get_context_data(**kwargs)
# Add in the publisher
if some_condition():
context['some_data'] = ...
else:
context['other_data'] = ...
return context
If all else fails and you're still determined to have class based views, you could probably also override get(self, request, *args, **kwargs) and do the switching there to the appropriate method. The detailed docs are getting better but I've still found myself poking through the source code to figure out how to accomplish what I want.

is system_user() defined in django?

My original question was actually how to add a User foreign key to Photolog type class (that uses Imagekit)
I see an answer to a similar question, but when I tried to implement it, I get global name 'system_user' is not defined
I'm not surprised by that, but I am surprised that though it's in an answer, I can't find a reference to system_user in django docs.
(It's not on docs.djangoproject.com, and Google for django+system_user returns nothing interesting.)
I have this in the class Photo in Photologue models.py
def save(self, *args, **kwargs):
if self.title_slug is None:
self.title_slug = slugify(self.title)
if 'owner' not in self.__dict__:
self.owner = system_user() # this line fails
super(Photo, self).save(*args, **kwargs)
How should I import system_user(), or what can I use here instead?
No, system_user is not a django function. You should take all code snippets as pseudo code -- he's just saying "a function that returns my object".
grep -ri "system_user" /path/to/django returns nothing, so it doesn't exist in the django source.
Check out the accepted answer in the question you linked to, he overrides the save method, passes in the user object, and manually associates the object to the user.
In your case, since you're using a model, you'd have to pass in the user object to the model save() method.
# models
def save(self, user=None, *args, **kwargs):
if self.title_slug is None:
self.title_slug = slugify(self.title)
if user:
self.owner = user
super(Photo, self).save(*args, **kwargs)
# usage in view
myobj.save(user=request.user)