Same as the title. What is the difference between the request and self in Django?
i'd try class based view and function view. and copy well-made code. but I don't know whay is the difference between self in fomr_valid and request in moneylog_delete definition.
class moneylog_update(UpdateView):
model = moneylog_models.Moneylog
form_class = forms.UpdateMoneylogForm
template_name = "moneylogs/update.html"
def form_valid(self, form):
moneylog = form.save(commit=False)
moneybook = moneybook_models.Moneybook.objects.get(
pk=self.kwargs["pk"])
moneylog.save()
form.save_m2m()
return redirect(reverse("moneybooks:detail", kwargs={'pk': moneybook.pk}))
def moneylog_delete(request, moneybook_pk, moneylog_pk):
user = request.user
try:
moneybook = moneybook_models.Moneybook.objects.get(pk=moneybook_pk)
models.Moneylog.objects.filter(pk=moneylog_pk).delete()
return redirect(reverse("moneybook:detail", kwargs={"pk": moneybook.pk}))
except models.Moneylog.DoesNotExist:
return redirect(reverse("cores:home"))
The request is an object that Django creates each time the real request was made to the view. It contains metadata about the real request and some attributes set by middleware (most commonly used is user instance).
self it's a Python way of naming the reference to the instance within the current scope. From doc's:
Often, the first argument of a method is called self. This is nothing more than a convention: the name self has absolutely no special meaning to Python. Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a class browser program might be written that relies upon such a convention.
Basically, these are two different things.
Related
I am trying to manually change a foreign key field (Supplier) of a model (Expenditure). I override the UpdateView post method of Expenditure and handle forms for other models in this method too. A new SupplierForm is also rendered in this view and I am tracking if this form is changed via has_changed() method of the form. If this form has changed, what I ask is overriding the related_supplier field of ExpenditureForm and picking newly created Supplier by this statement:
if supplier_form_changed:
new_supplier = related_supplier_form.save(commit=False)
new_supplier.save()
....
# This statement seems to have no effect
self.object.related_supplier = new_supplier
I override the post method with super(), so even though I explicitly state save() method for all related forms, however I don't call the save method of main model (Expenditure) since it is already handled after super(). This is what start and end of my method looks like;
def post(self, request, *args, **kwargs):
context = request.POST
related_receipt_form = self.receipt_form_class(context, request.FILES)
related_supplier_form = self.supplier_form_class(context, request.FILES)
self.object = self.get_object()
related_receipt = self.object.receipt
related_supplier_form = self.supplier_form_class(context)
expenditure_form = self.form_class(context)
inlines = self.construct_inlines()
....
return super().post(self, request, *args, **kwargs)
You may find the full code of my entire view here:
https://paste.ubuntu.com/p/ZtCfMHSBZN/
So my problem is self.object.related_supplier = new_supplier statement does not have any effect. After the update, old related_supplier object is still there, new one is saved but not attached to the updated Expenditure. Strange thing is I am doing a similar thing in the same view (also in CreateView) with receipt and no problem whatsoever.
I debugged the code via PyCharm, before the execution of super(), I can confirm that self.object.related_supplier is the newly created one, but when the super() executed, it returns back to the original supplier object.
you can override the form valid method to add things manually, an example shown below
def form_valid(self, form):
related_supplier_form.instance.related_supplier = new_supplier
valid_data = super(UpdateView, self).form_valid(form)
return valid_data
Need to clear a basic concept. In Django, what's the harm in redirecting to a view from form_valid() itself, instead of declaring a get_success_url()?
I.e. why is the following inferior, compared to what's below it:
class PostCreateView(CreateView):
model = Post
def form_valid(self, form):
# do something
return redirect("home")
class PostCreateView(CreateView):
model = Post
def form_valid(self, form):
# do something
return super(CreateView, self).form_valid(form)
def get_success_url(self):
return reverse("home")
Returning redirect isn't necessarily inferior. If you don't call super, then you need to save the form yourself. Duplicating two lines (save and return redirect) isn't really a problem. If super was more complicated then the duplication would be more of an issue, as there's a bigger chance of functionality being left out or errors being introduced.
On the plus side, returning the redirect response makes it obvious what form_valid will do, without having to look at what super does. Having control over how the form is saved can be useful too.
If your view might be subclassed, then you probably shouldn't return redirect, because you'll break any subclasses that override get_success_url.
I'm using the generic ListView of Django (1.9.1). I customized the name of the queryset (I called it content_list) to be put in the context. But surprisingly when I look at the context content, I can see object_list along with content_list. If the list is very big this is not very optimised. How can I get rid of object_list?. Here is my view:
class Home(ListView): #TemplateView
context_object_name = 'content_list'
template_name = 'website/index.html'
paginate_by = CONTENT_PAGINATE_BY
def get_queryset(self):
cc_id = self.kwargs.get('cc_id')
if cc_id != None:
qs = Content.objects.filter(category=cc_id)
else:
qs = Content.objects.all()
return qs.order_by('-created_on')
def get_context_data(self, **kwargs):
context = super(Home, self).get_context_data(**kwargs)
context['content_category_list'] = ContentCategory.objects.all()
print(context)
return context
I'm pretty sure they're both reference to the same list in memory.
From the docs:
Well, if you’re dealing with a model object, this is already done for you. When you are dealing with an object or queryset, Django is able to populate the context using the lower cased version of the model class’ name. This is provided in addition to the default object_list entry, but contains exactly the same data, i.e. publisher_list.
Aside from that, even if they weren't referencing the same data, you're forgetting that querysets are executed lazily so if you never use the other list then it is never executed.
This is by design. It's not another interaction to the database, but a second reference.
I am converting an old project with Class Views and I want to know if the pattern I am using is "safe".
In brief I have a class View like this (code simplified ):
class FileAddHashedView(FormView):
"""This view hashes the file contents using md5"""
form_class = FileUploadForm
success_url = reverse_lazy('vault-show')
template_name = "vault/add.html"
filebox_random= 0
def get_success_url(self):
return reverse('vault-show', kwargs={'random': self.filebox_random})
def form_valid(self, form):
instance = form.save(commit=False)
#generate a random number
rng = random.SystemRandom()
#TODO: must catch exception here
instance.random=rng.randint(0, sys.maxint)
instance.save()
#TODO: check what is the proper way to generate the parametric URL
self.filebox_random=instance.random
messages.success(
self.request, 'File hashed and uploaded!', fail_silently=True)
return super(FileAddHashedView, self).form_valid(form)
def form_invalid(self, form, **kwargs):
messages.error(self.request, 'Upload failed ...', fail_silently=True)
return super(FileAddView, self).form_invalid(form)
And the corresponding views that gets activated:
class VaultStatus(TemplateView):
template_name = 'vault/vault.html'
def get(self, request,random):
# retrieve object info
data = {
'filebox': FileBox.objects.get(random=random)
}
return render(request,self.template_name,data)
#return render_to_response('vault/vault.html', {'random': random});
Now my question is: is there a better way in get_success_url to get the self.filebox_random without relying on a class attribute?
Is this thread-safe?
You're not using a class attribute here. Even though you've defined filebox_random at class level, by setting a value to that name within an instance method in Python you're actually creating an instance attribute with the same name, which hides the class-level one. (Although I'm not actually sure why you're defining the class attribute in the first place).
Django does quite a lot of work behind the scenes to ensure that class-based views are thread-safe, so there is no danger in setting instance attributes.
again, apologies for what is probably a straightforward question!
Ok, so!
my problem is i have a saveModel function, where it saves a model. If the model is all good (is_valid), it will save the model and redirect to pageA
if the model is bad, or the request is a GET, then i'd like to redirect to pageB
all well and good, but i do this several times, how annoying! I don't want to cut and paste all the time, so i came up with this:
class SaveModel(View):
def as_view(self):
if request.method == "POST":
form = SaveModel.getPostForm(self.request)
if form.is_valid():
processedForm = SaveModel.processForm(self.request)
processedForm.save()
if (self.success_template):
return render_to_response(self.success_template)
else:
return render_to_response('pageA.html')
else:
form = SaveModel.getForm()
if (self.context_object_name):
contextName = context_object_name
else:
contextName = 'form'
if (self.template_name):
return render_to_response(template_name,{contextName:form})
else :
return render_to_response('pageB.html',{contextName:form})
def getForm(self):
return None
def getPostForm(self,request):
return None
def processForm(self,form,request):
return None
THEN, i define other classes to handle particular models, like, for example, so:
class StoryModelView(SaveModel):
def getForm(self,request):
return StoryForm()
def getPostForm(self,request):
return StoryForm(request.POST)
def processForm(self,form,request):
theStory = form.save(commit=False)
theStory.user = request.user
return theStory
and then, finally, in my urls.py i will refer to (as above) the model to use like so:
url(r'^addStory/$',
StoryModelView.as_view(
context_object_name='form',
template_name='accounts/addStory.html',
success_template='accounts/addStorySuccess.html'
)
),
This doesn't seem to work though - pycharm assures me that my references to self.context_object_name and so on are invalid. I'm v. new to python and django (which is why i thought i'd build a website with them! clever andrew!), so i am sure that i've missed a whole bunch of things (abstract methods and stuff... python does that, right?)
what do i need to do to get this all working? Is this how i should be doing things?
ANSWER BY ME!
Ok, so the comments everyone has written about the CreateView are probably correct. "Probably" because i never ended up using it, because i ended up sticking with my code instead.
In case anybody is, like me, new to python and django and wants to see how the whole thing works, here we are!
class SaveModel(View):
success_template = None
context_object_name = None
template_name = None
def post(self, request):
form = self.getPostForm(self.request)
if form.is_valid():
processedForm = self.processForm(form,self.request)
processedForm.save()
if self.success_template:
return render_to_response(self.success_template)
else:
return render_to_response('accounts/addStorySuccess.html')
else:
self.renderValidations(form)
def get(self,request):
form = self.getForm()
self.renderValidations(form)
def renderValidations(self,form):
if self.context_object_name:
contextName = self.context_object_name
else:
contextName = 'form'
if self.template_name:
return render_to_response(self.template_name,{contextName:form})
else :
return render_to_response('accounts/addStory.html',{contextName:form})
def getForm(self):
return None
def getPostForm(self,request):
return None
def processForm(self,form,request):
return None
and that is the main class, then i can override it like so:
class StoryModelView(SaveModel):
def getForm(self):
return StoryForm()
def getPostForm(self,request):
return StoryForm(request.POST)
def processForm(self,form,request):
theStory = form.save(commit=False)
theStory.user = request.user
return theStory
i tripped myself up with how "self" works in python a few times. it seems to be magically sent across with all method calls, but you need it as the first arg in the method declaration (but you never need to use it when calling/using the method)
i think there's only post or get for methods when overriding the View class. i don't have a good idea of the "process" of the call, or what the order is, dispatch was mentioned as something to override, but i suspect that is only where i need to change when/how to deal with differing request types (GET, POST, HEAD etc)
oh! the urls.py!
url(r'^addStory/$',
StoryModelView.as_view(
context_object_name='form',
template_name = 'accounts/addStory.html',
success_template= 'accounts/addStorySuccess.html'
)
),
i can just chuck whatever i want into that "as_view" call, and then, as long as those parameters are defined in the overriding class it's all good.
so yay! my classes all work and women want me. use my code, and this can happen to you too!*
*results atypical and fictional. your results may differ.