How to add signal method for my view? - django

I want to count how may files the user has uploaded.
I have added signals.py
from django.dispatch import Signal
upload_completed = Signal(providing_args=['upload'])
And summary.py
from django.dispatch import receiver
from .signals import upload_completed
#receiver(charge_completed)
def increment_total_uploads(sender, total, **kwargs):
total_u += total
to my project.
My views upload
#login_required
def upload(request):
# Handle file upload
user = request.user
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.uploaded_by = request.user.profile
upload_completed.send(sender=self.__class__, 'upload')
#send signal to summary
newdoc.save()
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('upload'))
else:
form = DocumentForm() # A empty, unbound form
# Load documents for the upload page
documents = Document.objects.all()
# Render list page with the documents and the form
return render(request,'upload.html',{'documents': documents, 'form': form})
This effort does not work.I got
upload_completed.send(sender=self.__class__, 'upload')
^
SyntaxError: positional argument follows keyword argument
I found signal example testing-django-signals
from .signals import charge_completed
#classmethod
def process_charge(cls, total):
# Process charge…
if success:
charge_completed.send_robust(
sender=cls,
total=total,
)
But it seems to me that classmethod would not work in my case
How to fix my method?

You don't need the 'uploads' argument for the send() method.
But a tip, if you're planning on a persistent count of the number of file uploads (which I assume you most likely are), then I think you should create a new model so that you can save it in your database.Then you can update that model every time a Document model is saved.
And I suggest you have a look at post_save. Have a nice day coding!

Related

django how to create instance of user

I am creating a simple application, successfully created for a single user, but I want to develop for multiple users, but unable to do so... i.e. user A can create his task and save in the database. similar B user can create tasks and store in the database. and each can see their own tasks I am newbie unable to proceed with the creation of instance can someone please guide me or point me to a blog ( searched online but no help ) I already asked this question however i was not clear ( django each user needs to create and view data Issue) hope now I am clear.
My model :
from django.contrib.auth.models import User, AbstractUser
from django.db import models
# Create your models here.
from django.db.models import Sum, Avg
class Task(models.Model):
user = models.ForeignKey(User, null=True,on_delete=models.CASCADE)
title = models.CharField(max_length=200)
complete = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True,auto_now=False,blank=True)
purchase_date = models.DateField(auto_now_add=False,auto_now=False,blank=True,null=True)
price = models.FloatField(max_length=5,default='0.00',editable=True)
def __str__(self):
return self.title
#classmethod
def get_price_total(cls):
total = cls.objects.aggregate(total=Sum("price"))['total']
print(total)
return total
Views
def createTask(request):
form = TaskForm()
if request.method =='POST':
form=TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('query')
context = {'form':form}
return render(request,'tasks/create.html',context)
def display(request):
# tasks = Task.objects.all()
tasks = request.user.task_set.all()
form = TaskForm()
if request.method =='POST':
if form.is_valid():
form.save()
return redirect('/')
# tasks= task.objects.all()
print("tasks",Task.get_price_total)
context = {'tasks':tasks,'form':form,'Totalprice':Task.get_price_total}
return render(request,'tasks/products.html',context)
urls
from django.urls import path
from . import views
urlpatterns = [
#path('', views.index, name="list"),
path('', views.query, name="query"),
path('create/', views.createTask, name="create"),
path('update_task/<str:pk>/', views.updateTask, name="update_task"),
path('register/', views.registerPage,name="register"),
path('login/', views.loginPage,name="login"),
path('logout/', views.logoutUser,name="logout"),
path('delete/<str:pk>/', views.deleteTask, name="delete"),
path('query/', views.query, name="query"),
path('products/', views.display, name="products"),
path('search/', views.showresults, name="search"),
]
Error (output)
Internal Server Error: /products/
Traceback (most recent call last):
File "C:\Users\s5114509\AppData\Local\Programs\Python\Python38\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\s5114509\AppData\Local\Programs\Python\Python38\lib\site-packages\django\core\handlers\base.py", line 124, in _get_response
raise ValueError(
ValueError: The view tasks.views.display didn't return an HttpResponse object. It returned None instead.
[21/Oct/2020 14:35:59] "GET /products/ HTTP/1.1" 500 60680
From what I heard it sounds lke you want to save a task when a user creates one. You don't want to include the user field in the form. You have to first get the model instance returned by the TaskForm.save() method. Now you have to assign the user field to the request.user since the user is authenticated. Here is how you can achieve that.
def createTask(request):
form = TaskForm()
task = None
if request.method =='POST':
form=TaskForm(request.POST)
if form.is_valid():
#Just get the Task instance do not make any commitments to database
task = form.save(commit=False)
#Now update the user
task.user = request.user
#Finally save to database
task.save()
return redirect('query')
context = {'form':form}
return render(request,'tasks/create.html',context)
On display you are getting an error because you are return a response only when a POST request is made. Check you identation and correct it. Identation mean scope in python. Check I have modified you code
def display(request):
#tasks = Task.objects.all()
tasks = request.user.task_set.all()
form = TaskForm()
if request.method =='POST':
if form.is_valid():
form.save()
return redirect('/')
#tasks= task.objects.all()
print("tasks",Task.get_price_total)
context = {'tasks':tasks,'form':form,'Totalprice':Task.get_price_total}
return render(request,'tasks/products.html',context)
Take a look at python identation

One Django Form in Multiple Views with Inherited Post Request

I haven't found any definitive documentation on this, but I have a contact form that I need to use in the sidebar in multiple views. Currently my code is dirty because I am repeating the code snippet below in multiple views to process the form. Is there a way to place the Post Request in a template that can be inherited?
View
def contact(request):
form_class = ContactForm
if request.method == 'POST':
form = form_class(data=request.POST)
messages.add_message(request, messages.SUCCESS, 'Thank you, we have received your message.')
if form.is_valid():
...
Thank you for your help.
Now, I'm assuming you do the same operation as below while every view is run:
def contact(request):
# First you choose the form.
form_class = ContactForm
# Then you want to know if request is POST type
if request.method == 'POST':
# You take the form data from given POST
form = form_class(data=request.POST)
# You add message to messages.
messages.add_message(request, messages.SUCCESS, 'Thank you, we have received your message.')
If you do the same over and over again, you can create your own function at the beginning of your views.py file of any app to make it short and not to repeat yourself again and again.
def take_message(request, form, messages, message):
if request.METHOD == "POST":
# I'm reinitializing <form> variable here.
form = form(data=request.POST)
# That <message> variable below must be a string, then you can dynamically pass your message.
messages.add_message(request, messages.SUCCESS, message)
Then use it in your view:
def contact(request):
take_message(request, ContactForm, messages, "Thanks, we got your message.")
# And the rest here.
However, I recommend for you to use class-based views since they can handle any request types as method. So, I'm changing that take_message method as below:
def take_message(request, form, messages, message):
# I'm reinitializing <form> variable here.
form = form(data=request.POST)
# That <message> variable below must be a string, then you can dynamically pass your message.
messages.add_message(request, messages.SUCCESS, message)
Then, my view is as below:
from django.views.generic import TemplateView
# And any other important imports.
# ...
class ContactView(TemplateView):
template_name = "contact.html" # This is your template.
def get(self, request):
# Do things when the method is GET. Like, viewing current messages in a hypothetical admin template.
def delete(self, request):
# Do things when the method is DELETE. Don't forget to use authentication here, so only superuser can delete messages.
def post(self, request):
# Do things when the method is POST.
# I'm assuming anonymous users can send messages, so there's no need for authentication here.
take_message(request, ContactForm, messages, "Thanks you, we got your message.")
# Other things to do.
# urls.py
url(r"^contact/$", ContactView.as_view(), name="contact-page")

Django 1.6 - reusable view with redirect

I have just made reusable comments app that can be attached to any model, please see view I have problem with ( I didn't include whole logic, only the one relevant to the question).
When there is no request.POST, all is working fine, however when I try to add the comment, after it is saved there is some problem with redirect, I am getting error
dictionary update sequence element #0 has length 0; 2 is required
problematic line is context.update(dictionary). It looks like it is empty when comment is added, but I don't understand why.
My logic is this:
when there is no request.POST, view add_comment will return
{'comment_form': comment_form, 'comments': comments}
when request.method==POST' , context.update(dictionary) shouldn't
be even executed, because of return redirect(node). It should result
in starting code executing in view profile, because that's where
redirect(node) should lead to.
I know I could just use redirect in profile.views.py, but then I will need to do this for every view with added comments, which is extremely unconvenient.
comment.views.py
from django.shortcuts import redirect
from comment.forms import AddCommentForm
from comment.models import Comment
def add_comment(request, node):
if request.user.is_authenticated():
user = request.user
else:
user = None
comment_form = None
comments = Comment.objects.get_comments(node) # custom manager method for getting all comments
if user:
if request.method == 'POST':
comment_form = AddCommentForm(request.POST)
if comment_form.is_valid():
comment_form.save(node=node, user=user) # custom form save method, updating missing fields
return redirect(node) #redirect to node.get_absolute_url()
else:
comment_form = AddCommentForm()
return {'comment_form': comment_form, 'comments': comments}
profile.views.py - another app, I want to reduce the code for adding comment by only referring to view add_comment
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.models import User
from comment.views import add_comment
def profile(request, id):
user = get_object_or_404(User, id=id)
dictionary = add_comment(request, user)
context = {'user': user}
context.update(dictionary) #problematic line
return render(request, 'profile/profile account.html', context)
The problem is that add_comment can return something that isn't a dictionary: that is, a redirect (which is a subclass of HttpResponse). You could always check the type of what is returned before using it:
result = add_comment(request, user)
if not isinstance(result, dict):
return result
else:
context.update(result)

Handling form from different view and passing form validation through session in django

I have a requirement here to build a comment-like app in my django project, the app has a view to receive a submitted form process it and return the errors to where ever it came from. I finally managed to get it to work, but I have doubt for the way am using it might be wrong since am passing the entire validated form in the session.
below is the code
comment/templatetags/comment.py
#register.inclusion_tag('comment/form.html', takes_context=True)
def comment_form(context, model, object_id, next):
"""
comment_form()
is responsible for rendering the comment form
"""
# clear sessions from variable incase it was found
content_type = ContentType.objects.get_for_model(model)
try:
request = context['request']
if request.session.get('comment_form', False):
form = CommentForm(request.session['comment_form'])
form.fields['content_type'].initial = 15
form.fields['object_id'].initial = 2
form.fields['next'].initial = next
else:
form = CommentForm(initial={
'content_type' : content_type.id,
'object_id' : object_id,
'next' : next
})
except Exception as e:
logging.error(str(e))
form = None
return {
'form' : form
}
comment/view.py
def save_comment(request):
"""
save_comment:
"""
if request.method == 'POST':
# clear sessions from variable incase it was found
if request.session.get('comment_form', False):
del request.session['comment_form']
form = CommentForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
if request.user.is_authenticated():
obj.created_by = request.user
obj.save()
messages.info(request, _('Your comment has been posted.'))
return redirect(form.data.get('next'))
else:
request.session['comment_form'] = request.POST
return redirect(form.data.get('next'))
else:
raise Http404
the usage is by loading the template tag and firing
{% comment_form article article.id article.get_absolute_url %}
my doubt is if am doing the correct approach or not by passing the validated form to the session. Would that be a problem? security risk? performance issues?
Please advise
Update
In response to Pol question. The reason why I went with this approach is because comment form is handled in a separate app. In my scenario, I render objects such as article and all I do is invoke the templatetag to render the form. What would be an alternative approach for my case?
You also shared with me the django comment app, which am aware of but the client am working with requires a lot of complex work to be done in the comment app thats why am working on a new one.
I dont see the problem with security, except situation when you using cookies for stroring session. The performance depends on what kind of session backand you are using as well. But I cant find the point why are you complicating things!
And I dont thing that touching session in template tag is a good idea at all.
And maybe Take a look at django Comments Framework
Update:
Ok. I cant see the problems in this approach except complication. For example in my project, i'm using ajax to send data and validate it right in the comments view, therefore I do not require to redirect to original page. Other thing is that I pass the initialized Form in article view, so i'm not using templatetags.
Can provide you with my approche for example purposes:
from forms import CommentForm
from models import Comment
from django.http import HttpResponseForbidden, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import simplejson
from etv_persons.person.models import Person
from django.contrib import messages
def create_comment(request,slug):
if request.method != 'POST' or not request.POST or request.user.is_anonymous():
return HttpResponseForbidden('Доступ запрещен')
person = get_object_or_404(Person,slug=slug)
form = CommentForm(data=request.POST)
if form.is_valid():
Comment.objects.create(user_id=request.user.id, person=person,text=form.cleaned_data['text'])
if request.is_ajax():
msg={'msg': 'Cement was send',}
else:
messages.info(request, 'COmment was send.')
else:
if request.is_ajax(): msg={'msg': 'Error.',}
else: messages.info(request, 'Error.')
if request.is_ajax():
return HttpResponse(simplejson.dumps(msg),content_type='application/json')
else:
return redirect('person_details',**{"slug":slug,"ptype":person.type})
And in the article view we just do:
response['comment_form'] = CommentForm()
And yes, I do not validate the comments form. There is no reason. Just one text input.

django TemplateView and form

I have some problem to figure out how new django views (template view) and forms can works I also can't find good resources, official doc don't explain me how can get request ( I mean get and post) and forms in new django views class
Thanks
added for better explain
for example I have this form :
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
and this is the code for read and print the form (old fashion way):
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
well my question is how you can do the same thing with template view thanks
Use a FormView instead, i.e.
from django.views.generic import TemplateView, FormView
from forms import ContactUsEmailForm
class ContactView(FormView):
template_name = 'contact_us/contact_us.html'
form_class = ContactUsEmailForm
success_url = '.'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
#context["testing_out"] = "this is a new context var"
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
#form.send_email()
#print "form is valid"
return super(ContactView, self).form_valid(form)
More on FormView in Django Docs
Technically TemplateView can also be used, just overwrite the post method, since by default template view does not allow you to post to it:
class ContactUsView(TemplateView):
template_name = 'contact_us/contact_us.html'
def post(self, request, *args, **kwargs):
context = self.get_context_data()
if context["form"].is_valid():
print 'yes done'
#save your model
#redirect
return super(TemplateView, self).render_to_response(context)
def get_context_data(self, **kwargs):
context = super(ContactUsView, self).get_context_data(**kwargs)
form = ContactUsEmailForm(self.request.POST or None) # instance= None
context["form"] = form
#context["latest_article"] = latest_article
return context
I think the FormView makes more sense though.
I would recommend just plodding through the official tutorial and I think realization will dawn and enlightenment will come automatically.
Basically:
When you issue a request: '''http://mydomain/myblog/foo/bar'''
Django will:
resolve myblog/foo/bar to a function/method call through the patterns defined in urls.py
call that function with the request as parameter, e.g. myblog.views.foo_bar_index(request).
and just send whatever string that function returns to the browser. Usually that's your generated html code.
The view function usually does the following:
Fill the context dict for the view
Renders the template using that context
returns the resulting string
The template generic view allows you to skip writing that function, and just pass in the context dictionary.
Quoting the django docs:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
All views.generic.*View classes have views.generic.View as their base. In the docs to that you find the information you require.
Basically:
# urls.py
urlpatterns = patterns('',
(r'^view/$', MyView.as_view(size=42)),
)
MyView.as_view will generate a callable that calls views.generic.View.dispatch()
which in turn will call MyView.get(), MyView.post(), MyView.update() etc.
which you can override.
To quote the docs:
class View
dispatch(request, *args, **kwargs)
The view part of the view -- the method that accepts a request
argument plus arguments, and returns a HTTP response. The default
implementation will inspect the HTTP method and attempt to delegate to
a method that matches the HTTP method; a GET will be delegated to
get(), a POST to post(), and so on.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
The big plusses of the class based views (in my opinion):
Inheritance makes them dry.
More declarative form of programming