Django CreateUpdateView implementation in the django template - python

I am very new to django Class based views, trying to integrate it with my existing project.
My goal is to use same Class based view to Create and Update student application form.
I am trying to integrate CreateUpdateView from the #scubabuddha's answer from the solution.
views.py
from createupdateview import CreateUpdateView
class StudentView(CreateUpdateView):
template_name="admission.html"
Model = Student
form_class = StudentForm
def get(self, request, *args, **kwargs):
return self.post(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
forms = {"userform": UserForm(request.POST or None), guardian..., contact..., # other 5-6 forms}
if request.POST:
invalid_forms = [form.is_valid() for form in self.forms.values()]
if not all(invalid_forms):
messages.error(request,'You must fill up all the valid mandatory form fields!')
return render(request, 'admission.html', forms)
#-- Logic to validate and save each form
...
return render(request, 'admission.html', forms)
return render(request, 'admission.html', forms)
This is perfectly working for CreateView, but not able to understand how to use this for UpdateView or editview. If user hits editview, {{form|crispy}} should also display the details to allow user to edit the form.
urls.py (I also want to merge below 2 urls to 1, can we do this?)
from django.urls import path
from students import views
from students.views import StudentList, StudentView
urlpatterns = [
path('', StudentList.as_view(), name='students'),
path('add/', StudentView.as_view(), name='addview'),
path('<int:pk>/edit/', StudentView.as_view(), name='editview'),
...
]
I want to display, all the student details in the UpdateView forms -
admission.html
<form class="form" name="admissionForm" id="admissionForm" method="post"
enctype="multipart/form-data" action="{% url 'addview' %}">
{% csrf_token %}
<div class="pages">
<h4 class="mt-2 mb-3">Personal Information</h4>
{% include "student_errors.html" %}
{{userform|crispy}} #-- It should display student details
{{guardian_form|crispy}}
{{contact_form|crispy}}
....
<div class="btn_container">
<button type="submit" class="btn btn-info float-right btn-next">Submit</button>
</div>
</div>
P.S. I kept minimal code here, in actual production its huge code. (Migrating Django applcation 1.9 to 3.0)

The CreateUpdateView you are using from this solution inherits from ModelFormMixin and expect to handle only one form (initialization, form_class, saving, ...). And in your code your are rewriting get() and post() method so it make no sense to inherit from CreateUpdateView.
Here is how you can do it using a simple View (untested) :
from django.http import Http404
from django.views.generic import View
from django.shortcuts import render
class StudentView(View):
template_name = "admission.html"
def get_object(self):
if self.kwargs.get('pk'):
try:
obj = Student.objects.get(pk=pk)
except Student.DoesNotExist:
raise Http404("No student found matching the query")
else:
return obj
return None # create view
def get_view(self, request, *args, **kwargs):
forms = {"userform": UserForm(request.POST or None, instance=self.object), guardian..., contact...} # other 5-6 forms}
if request.POST:
invalid_forms = [form.is_valid() for form in self.forms.values()]
if not all(invalid_forms):
messages.error(request,'You must fill up all the valid mandatory form fields!')
else:
#-- Logic to validate and save each form
return render(request, self.template_name, forms)
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return self.get_view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return self.get_view(request, *args, **kwargs)

Related

Django : What is the difference in the return output from the post method and form_valid method?

I have a form to upload a file. This file is stored in a azure blob storage without using media folders.
In the html, the form send the request to a specific FormView :
<form action="{% url 'intervenant_upload' id_intervenant %}"
method="post"
enctype="multipart/form-data"
id="form-file-upload">
{% csrf_token %}
{{ form|crispy }}
<div class="row">
<button
type="submit"
class="btn btn-primary btn-block"
id="submitupload"
>
Charger
<i class="bi bi-upload" style="font-size: 17px"></i>
</button>
</div>
</form>
The view (in Class Base View) works well, it fills the database and upload the file to the blob storage. However I have a problem when I do the return to end the process. I got this error :
didn't return an HttpResponse object. It returned None instead.
I do not understand why because I have the impression to respect the structure of the FormView according thie website https://ccbv.co.uk/
Here is the my FormView that does not work :
class IntervenantFormUpload(FormView) :
model = FilesIntervenants
form_class = FilesIntervenantForm
def get_success_url(self) :
return reverse('intervenants_image_upload', kwargs={'pk': self.id_intervenant})
def setup(self, request, *args, **kwargs) :
self.id_intervenant = kwargs['pk']
self.user = request.session.get('user')
return super().setup(request, *args, **kwargs)
def post(self, request, *args, **kwargs) :
form = self.form_class(request.POST, request.FILES)
if form.is_valid() :
self.form_valid(form)
else :
self.form_invalid(form)
def form_valid(self, form) :
BLABLA-BLABLA-BLABLA
return super(IntervenantFormUpload, self).form_valid(form)
def form_invalid(self, request, form):
context = self.get_context_data()
context['form'] = form
return render(request, 'upload/upload.html', context)
However, when I do just a post method that return a redirect, it works. Here is the code that works :
class IntervenantFormUpload(FormView) :
model = FilesIntervenants
form_class = FilesIntervenantForm
def post(self, request, *args, **kwargs) :
form = self.form_class(request.POST, request.FILES)
files_selected = self.request.FILES.getlist('Filename')
if form.is_valid():
return redirect(reverse('intervenants_image_upload', kwargs={ 'pk': self.id_intervenant}))
So what is the difference ?
Thank you

Django Form in ListView, error on POST of Form

Im trying to implement a chat-function for my website. In order to do that, i followed the following tutorial: https://channels.readthedocs.io/en/latest/tutorial/
I've then changed the code a little bit in order to implement it. Until here, everything works just fine. Now I want to store the form-data inside of a database, and thats where the problem appears.
But first my code:
urls.py:
from django.urls import path
from .views import ChatOverView
urlpatterns = [
path('<int:pk>/', ChatOverView.as_view(), name='chat-explicit'),
path('', ChatOverView.as_view(), name='chat-home'),
]
views.py (theres much code here that is probably not needed for this question, but since i dont know what part of it i can ignore, im just posting the whole file-content):
from django.views.generic import ListView
from django.views.generic.edit import FormMixin, FormView
from django.db.models import Q
from django.urls import resolve
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from .models import Message
from .forms import MessageRegisterForm
class ChatOverView(ListView, FormMixin):
model = Message
template_name = 'chat/home-chat.html'
form_class = MessageRegisterForm
success_url = '/thanks/'
def form_valid(self, form):
form = self.get_form()
if form.is_valid():
data = form.cleaned_data
return super().form_valid(form)
def get_context_data(self, *args, **kwargs):
context = super(ChatOverView, self).get_context_data(*args, **kwargs)
messages_raw = reversed(Message.objects.filter(Q(sender=self.request.user) | Q(receiver=self.request.user)))
messages = {}
for mes in messages_raw:
# i am receiver
if mes.sender != self.request.user:
if mes.sender in messages:
messages[mes.sender].append({"received": mes})
else:
messages.update({mes.sender: [{"received": mes}]})
# i sent
else:
if mes.receiver in messages:
messages[mes.receiver].append({"sent": mes})
else:
messages.update({mes.receiver: [{"sent": mes}]})
active_user = self.get_active_chat(messages)
chatroom_name = self.get_chatroom_name(active_user)
context.update(messages_data=messages, active=active_user, roomname=chatroom_name)
return context
def get_chatroom_name(self, active_chat):
# my convention
ids = [active_chat.id, self.request.user.id]
ids.sort()
return str(ids[0]) + '_' + str(ids[1])
def get_active_chat(self, messages):
url_name = resolve(self.request.path_info).url_name
if url_name == "chat-home":
return list(messages.keys())[0]
else:
pk_user = self.request.build_absolute_uri().split("/")[-2]
user = get_object_or_404(User, pk=pk_user)
return user
home-chat.html:
{% extends "solawi/base.html" %}
{% load define_dictfilters %}
{% block content %}
<form class="bg-light" method="post">
<div class="input-group">
{% csrf_token %}
{{ form }}
<div class="input-group-append">
<button type="submit" id="chat-message-submit" value="enter">send</button>
</div>
</div>
</form>
{% endblock content %}
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Message(models.Model):
sender = models.ForeignKey(User, related_name="sender", on_delete=models.CASCADE)
receiver = models.ForeignKey(User, related_name="receiver", on_delete=models.CASCADE)
content = models.TextField()
date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.sender.username + ' to ' + self.receiver.username
The rest of the files/settings entrys are the same as in the tutorial linked above.
Now comes the problem: when submitting the form, i get the following error:
Method Not Allowed (POST): /chat/
Method Not Allowed: /chat/
HTTP POST /chat/ 405 [0.00, 127.0.0.1:54424]
How can i fix it?
Thank you for your Help!!
ListView implements a get() method, but no post() method. You need to implement a post() method in order for the view to allow POST requests.
You could subclass django.views.generic.ProcessFormView to get this, or, if you really need a ListView, then you can add a post() method to the class which handles the form validation and whatever else you need to do. Here is how ProcessFormView implements it:
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)

Combining two separate forms in one Django view?

This question have been answered before, e.g here: Proper way to handle multiple forms on one page in Django
So before it gets marked as a duplicate. I'll try to explain why its different.
I've got three tables, Project, ProjectUser and User. ProjectUser is a join table to indicate what users belongs to what project.
I'm trying to create a view that lets users update project details (e.g. name of project), and also add users to the project (which is indicated by a dropdown that shows all available users like the standard one for models with foreign keys in the django admin panel). All works fine until I'm trying to pass an id from the views to the formclass and submit.
views.py
class ProjectUpdateView(UpdateView):
form_class = ProjectUpdateForm
second_form_class = ProjectUserAddForm
template_name = 'projects/project_edit.html'
success_url = reverse_lazy('projects:list')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
id_ = self.kwargs.get("id")
project = Project.objects.get(id=id_)
if 'form' not in context:
context['form'] = self.form_class()
if 'form2' not in context:
team = Organization.objects.get(id=project.organization_id)
context['form2'] = self.second_form_class(queryset=team) # <-- here is where I wish to pass a queryset, which fails when trying to submit form2.
context['project_users'] = ProjectUser.objects.filter(project__id=project.id).select_related("project")
context['team'] = Organization.objects.get(id=project.organization_id)
return context
def get_object(self):
id_ = self.kwargs.get("id")
return get_object_or_404(Project, id=id_)
def form_invalid(self, **kwargs):
return self.render_to_response(self.get_context_data(**kwargs))
def form_valid(self, form):
project_id = self.kwargs.get("id")
if self.request.POST.get("form2") == 'Add':
ProjectUser.objects.create(user_id=self.request.POST.get("user"), project_id=project_id)
form.save()
success_url = reverse("projects:edit", args=(project_id,))
return HttpResponseRedirect(success_url)
def post(self, request, *args, **kwargs):
# get the user instance
self.object = self.get_object()
# determine which form is being submitted
# uses the name of the form's submit button
if 'form' in request.POST:
# get the primary form
form_class = self.get_form_class()
form_name = 'form'
else:
# get the secondary form
form_class = self.second_form_class
form_name = 'form2'
# get the form
form = self.get_form(form_class)
# validate
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(**{form_name: form})
projects_edit.html
<form action="{% url 'projects:edit' project.id %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form.name|as_crispy_field}}
<input name="form" value="Update" type="submit"></input>
</form>
<form action="{% url 'projects:edit' project.id %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form2.user}}
<input name="form2" value="Add" type="submit"></input>
</form>
forms.py
class ProjectUpdateForm(ModelForm):
class Meta:
model = Project
fields = ["name"]
class ProjectUserAddForm(ModelForm):
def __init__(self, queryset, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['user'].queryset = User.objects.filter(organizations_organizationuser__organization__id=queryset.id) # here is where I wish to pass the id of the queryset from the form class
class Meta:
model = ProjectUser
fields = ["user"]
Rendering the forms works just fine with the desired queryset, but when I try to submit the second form (adding a user to the ProjectUserForm, I just get a
__init__() missing 1 required positional argument: 'queryset' error.
Any ideas on how to solve this? Perhaps I'm making it way more complicated than it should
I have also added a screenshot if it helps: https://imgur.com/a/uqu0UeB

I wanna load results.html when I put vote button

I am making a web site by seeing Django tutorial.
I wanna load results.html when I put vote button is in detail.html ,but now index.html is loaded.detail.html is
{% extends "polls/base.html" %}
{% load bootstrap3 %}
{% block contents %}
<h1>{{ question.question_text }}</h1>
<!--{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}-->
<form action="{% url 'polls:poll_vote' question.id %}" method="post">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" value="Vote" />
</form>
{% endblock %}
When I put vote button is this code <input type="submit" value="Vote" />,
I wanna show results.html .
views.py is
from django.shortcuts import render
from django.urls import reverse_lazy
from django.utils.html import mark_safe
from .models import Question
from django.http import HttpResponse
from django.shortcuts import Http404
from django.shortcuts import get_object_or_404,redirect
from .models import Choice
from django.views.generic import TemplateView
from django.views.generic import DetailView
from django.views.generic import ListView
from .forms import MyForm
from .forms import VoteForm
from django.views.generic import FormView
from django.views.generic.detail import SingleObjectMixin
from django.shortcuts import resolve_url
from django.contrib import messages
# Create your views here.
def index(request):
return render(request,'polls/index.html',{
'questions': Question.objects.all(),
})
def vote(request,pk):
question = get_object_or_404(Question,pk=pk)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError,Choice.DoesNotExist):
return render(request,'poll/detail.html',{
'question':question,
'error_message':"You didn't select a choice",
})
else:
selected_choice.votes += 1
selected_choice.save()
return redirect('index')
return redirect('poll_results', pk)
# pass
def results(request,pk):
obj = get_object_or_404(Question,pk=pk)
return render(request,'polls/results.html',{
'question':obj,
})
class FormTest(FormView):
form_class = MyForm
template_name = 'polls/form.html'
success_url = reverse_lazy('polls:index')
form_test = FormTest.as_view()
class Detail(SingleObjectMixin,FormView):
model = Question
form_class = VoteForm
context_object_name = 'question'
template_name = 'polls/detail.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super().post(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super().post(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['question'] = self.object
return kwargs
def form_valid(self, form):
form.vote()
choice = form.cleaned_data['choice']
messages.success(self.request,'"%s"に投票しました' % choice)
return super().form_valid(form)
def get_success_url(self):
return resolve_url('polls:results',self.kwargs['pk'])
detail = Detail.as_view()
I think def vote(request,pk) is read,so return redirect('poll_results', pk) is also read and results.html is load.But my ideal flow is not realized.Was I wrong to write directory?
Directory is
How can I fix this?return redirect('polls_results', pk) did not work.
Now I received one answer,
vote method in views.py is
def vote(request,pk):
question = get_object_or_404(Question,pk=pk)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError,Choice.DoesNotExist):
return render(request,'poll/detail.html',{
'question':question,
'error_message':"You didn't select a choice",
})
else:
selected_choice.votes += 1
selected_choice.save()
return redirect('index')
return redirect(reverse('polls_results'), pk=pk)
but same error happens.
urls.py in polls is
from django.conf.urls import url
from django.views.generic import TemplateView
from . import views
app_name="polls"
urlpatterns = [
url(r'(?P<pk>\d+)/$', views.detail, name='poll_detail'),
url(r'(?P<pk>\d+)/vote$', views.vote, name='poll_vote'),
url(r'(?P<pk>\d+)/results$', views.results, name='poll_results'),
url(r'^$',views.index,name='index'),
url(r'^form$', views.form_test),
]
It seems that you are using namespaced urls.
In that case, you should use the namespace prefix whenever you use reverse, redirect or {% url %}.
return redirect('polls:polls_results', pk=pk)
Use reverse or write the correct path in redirect method.
Example:
from django.urls import reverse
def the_view(request):
# change here
return redirect(reverse('poll_results'), pk=pk)
# or
return redirect('results_url', pk=pk)
EDIT: Based on your urlpatterns, it seems that you're passing the wrong name in the reverse method, polls_results, where as you've defined it to be as poll_results.
You haven't removed the redirect to index statement in else part of the code in vote method.
def vote(request, pk):
# your code
else:
# save to model
# remove redirect statement here
return redirect(reverse('poll_results'), pk=pk)
Ref: https://docs.djangoproject.com/en/1.11/ref/urlresolvers/#reverse

Django: copy a model object using views and forms

I have a Django (1.8) model that has some class based generic views: list, update, delete, detail, create.
https://docs.djangoproject.com/en/1.8/ref/class-based-views/
On the detail or list view, I have a button that I want to do this:
Create a copy of the object
Load the data into a form and display for the user to edit/save the new object (could use the existing update or create views, or a new one?)
I can clone the model with this info:
How do I clone a Django model instance object and save it to the database?
But I can't make the leap to get this done by starting at a view and ending at a form with the copied object data.
Thanks!
partial views.py
class List(ListView):
model = Announcement
template_name = 'announcements/list.html'
class Create(CreateView):
model = Announcement
form_class = AnnouncementForm
template_name = 'announcements/form.html'
def form_valid(self, form):
data = form.save(commit=False)
data.author = self.request.user
data.save()
return super(Create, self).form_valid(form)
class Update(UpdateView):
model = Announcement
form_class = AnnouncementForm
template_name = 'announcements/form_update.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(Update, self).dispatch(*args, **kwargs)
partial forms.py
class AnnouncementForm(forms.ModelForm):
class Meta:
model = Announcement
exclude = ['author']
partial list.html
{% for object in object_list %}
<p>object.title</p>
<a class="btn btn-danger" href="{% url 'announcements:delete' object.id %}" role="button">Delete</a>
<a class="btn btn-info" href="{% url 'announcements:update' object.id %}" role="button">Edit</a>
<a class="btn btn-primary" href="" role="button">Copy</a>
{% endfor %}
What I hit the "Copy" button in list.html, I want to duplicate the object and open the new duplicate in a form for editing.
It think I figured it out!
urls.py
#eg: myapp/5/copy/
#where 5 is the item I want to copy
url(r'^(?P<id>[0-9]+)/copy/$', views.item_copy, name='item_copy'),
views.py:
def item_copy(request, id):
new_item = get_object_or_404(MyModel, pk = id)
new_item.pk = None #autogen a new pk (item_id)
new_item.name = "Copy of " + new_item.name #need to change uniques
form = MyForm(request.POST or None, instance = new_item)
if form.is_valid():
form.save()
return redirect('my_view')
context = {
"form": form,
#other context
}
return render(request, "form.html", context)
class CopyView(ManageAnnouncement, DeleteView):
def dispatch(self, *args, **kwargs):
obj = self.get_object()
obj.pk = None
copy = obj.save()
return HttpResponseRedirect('/announcement/edit/%s' %(copy.id))
# Change the redirect page to the one you need.
I have inherited a base class called ManageAnnouncement. You can put methods or variables common to multiple classes in an abstract base class and inherit it in adding, editing deleting, copying etc, so that code gets 'dry'.

Categories