How to pass form to Class-based generic views(TodayArchiveView)? - python

url.py
from django.conf.urls import patterns, include, url
import os.path
from crm.views import *
urlpatterns += patterns('',
(r'^test/$', tView.as_view()),
)
views.py
from django.views.generic import TodayArchiveView
from crm.forms import *
from crm.models import *
class tView(TodayArchiveView):
model = WorkDailyRecord
context_object_name = 'workDailyRecord'
date_field = 'date'
month_format = '%m'
template_name = "onlyWorkDailyRecord.html"
form_class = WorkDailyRecordForm ################## ADD
tView is generic view....
WorkDailyRecord is defining model in models.py
I want to pass form('WorkDailyRecordForm' class in forms.py) to template(onlyWorkDailyRecord.html)
How??
add
forms.py
from django import forms
class WorkDailyRecordForm(forms.Form):
contents = forms.CharField(label='',
widget=forms.Textarea(attrs={'placeholder': 'contents', 'style':'width:764px; height:35px;'}),
required=False,
)
target_user = forms.CharField(label='',
widget=forms.TextInput(attrs={'placeholder': 'target', 'style':'width:724px'}),
required=False,
)
onlyWorkDailyRecord.html
<form method="post" action="." class="form-inline" id="save-form">
{{ form.as_table }}
<button type="submit" class="btn btn-mini" id="workDailyRecord-add">작성</button>
</form>

In the class based views you need to mention the form_class:
form_class = WorkDailyRecordForm
A simple example:
class tView(TodayArchiveView):
form_class = WorkDailyRecordForm
template_name = 'onlyWorkDailyRecord.html'
model = WorkDailyRecord
def form_valid(self, form, **kwargs):
instance = form.save(commit=True)
return HttpResponse('Done')
def dispatch(self, request, *args, **kwargs):
return super(tView, self).dispatch(request, *args, **kwargs)
For more information read generic-editing in class based views

I had trouble with passing form class into a generic, I was able to do it by overriding the get_context_data() function, in case anyone was looking for this as well.
An example from the mozilla documentation:
class BookListView(generic.ListView):
model = Book
def get_context_data(self, **kwargs):
# Call the base implementation first to get the context
context = super(BookListView, self).get_context_data(**kwargs)
# Create any data and add it to the context
context['some_data'] = 'This is just some data'
return context
You would pass the form object into a key in the context dictionary.
Can find more information here.

Related

Django form is valid() returns false

I am trying to do multiple file uploads in Django but am encountering some errors: form.is_valid() returning false. <br>
I have tried printing form.erorrs and it gives me the following:
<ul class="errorlist"><li>uploaded_images<ul class="errorlist"><li>“b'\xbd\x06D\xcd3\x91\x85,\xdf\xa5K\n'” is not a valid value.</li></ul></li></ul>
I am not sure how to interpret this general error. Been searching for quite some time but couldn't find an answer.
Edit:
Im also encountering a "This field is required." error
Perhaps this screenshot may help with debugging:
I must be missing something simple but I just can't find it!!
Another pair of eyes to help me sieve through would be very appreciated!
views.py
from django.shortcuts import render,redirect
from django.views.generic.edit import FormView
from .forms import BRForm
from .models import *
class BRHomeView(FormView):
form_class = BRForm
model = TargetImage
template_name = 'br/br-home.html'
context_object_name = 'bankrecon'
def get(self, request):
form = BRForm(request.POST, request.FILES)
return render(request, self.template_name, {'form': form, 'title': 'Bank Reconcilation'})
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('uploaded_images')
print("POST")
print(request.POST)
print("FILES")
print(request.FILES)
print(form.errors)
if form.is_valid():
form.save()
return redirect('br-home')
models.py
from django.db import models
# Create your models here.
class UploadedImages(models.Model):
image_files = models.ImageField(null=True, blank=True, upload_to='images/')
class TargetImage(models.Model):
invoice_date = models.DateTimeField()
recon_date = models.DateTimeField()
uploaded_images = models.ManyToManyField(UploadedImages)
def __str__(self):
return self.invoice_date
forms.py
from django import forms
from django.core.validators import FileExtensionValidator
from .models import *
class BRForm(forms.ModelForm):
class Meta:
model = TargetImage
fields = ('invoice_date', 'recon_date', 'uploaded_images')
widgets = {
'invoice_date': forms.DateInput(attrs={'type': 'date'}),
'recon_date': forms.DateInput(attrs={'type': 'date'}),
'uploaded_images': forms.ClearableFileInput(attrs={'multiple': True, 'accept':'.jpeg, .png, .jpg'}),
}
relevant template code:
<div class="mt-5 d-flex justify-content-center">
<form action="" enctype="multipart/form-data" method="POST">
{% csrf_token %}
{{ form.as_p }}
<div class="d-flex justify-content-center">
<button class="btn btn-success mt-3" type="submit">Submit</button>
</div>
</form>
</div>
Extra info: My request.POST and request.FILES seems to be working fine:
POST
<QueryDict: {'csrfmiddlewaretoken': ['hiding this'], 'invoice_date': ['2021-02-01'], 'recon_date': ['2021-02-01']}>
FILES
<MultiValueDict: {'uploaded_images': [<InMemoryUploadedFile: first_slide (1).jpg (image/jpeg)>, <InMemoryUploadedFile: fourth_slide (1).jpg (image/jpeg)>]}>
Thank you all!!
Some errors that I see in your code:
You're trying to render a POST request in your get method.
You're not passing any data from POST request to your form instance. That's why is_valid() is returning False.
Change your code to something like this and see if it works:
...
def get(self, request):
form = BRForm()
return render(request, self.template_name, {'form': form, 'title': 'Bank Reconcilation'})
def post(self, request, *args, **kwargs):
form = BRForm(request.POST)
if form.is_valid():
form.save()
return redirect('br-home')
I have managed to solve this issue! <br>
Link: https://stackoverflow.com/a/60961015/10732211 <br>
I overhauled the entire thing and followed the above link. Probably the models relations have some issues that does not work. <br>
views.py
class BRHomeView(FormView):
# model = TargetImage
template_name = 'br/br-home.html'
context_object_name = 'bankrecon'
def get(self, request):
form = BRFormExtended()
return render(request, self.template_name, {'form': form, 'title': 'Bank Reconcilation'})
def post(self, request, *args, **kwargs):
form = BRFormExtended(request.POST,request.FILES)
files = request.FILES.getlist('image_files')
if form.is_valid():
print("Form Valid")
print(form.cleaned_data['invoice_date'])
print(form.cleaned_data['recon_date'])
user = request.user
invoice_date = form.cleaned_data['invoice_date']
recon_date = form.cleaned_data['recon_date']
target_image_obj = TargetImage.objects.create(user=user,invoice_date=invoice_date, recon_date=recon_date)
for file in files:
UploadedImage.objects.create(target_image=target_image_obj,image_files=file)
else:
print("Form Invalid")
return redirect('br-home')
forms.py
class BRForm(forms.ModelForm):
class Meta:
model = TargetImage
fields = ['invoice_date', 'recon_date']
widgets = {
'invoice_date': forms.DateInput(attrs={'type': 'date'}),
'recon_date': forms.DateInput(attrs={'type': 'date'}),
}
class BRFormExtended(BRForm):
image_files = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
class Meta(BRForm.Meta):
fields = BRForm.Meta.fields + ['image_files',]
models.py
from django.contrib.auth.models import User
from django.utils import timezone
class TargetImage(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
uploaded_date = models.DateTimeField(default=timezone.now)
invoice_date = models.DateTimeField()
recon_date = models.DateTimeField()
def __str__(self):
return self.user.__str__()
class UploadedImage(models.Model):
target_image = models.ForeignKey(TargetImage, on_delete=models.CASCADE)
image_files = models.ImageField(null=True, blank=True, upload_to='images/')
Also, it would be helpful to have a media root folder and edits to urls.py is also required.
urls.py
from django.urls import path
from .views import BRHomeView
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('', BRHomeView.as_view(), name='br-home'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
STATIC_URL = '/static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
Hopefully this will help someone!

How to place search query on url on django?

I am creating a search application with Django.
I made an article model and a Feedback model that records the rating of articles.
After entering search box and displaying the search results, click one of the results then goes to the detail screen.
After selecting feedback on the detail screen and pressing the submit button, I want to save a search query to the feedback model.
I think that solution is to add a query in the URL like portal/search/?=query and read it, but I don't know how to code it. Also, could you teach me if there is an implementation method other than reading query in the URL?
Also, when I go back from the detail screen, I want to display the previous search results too.
Please comment if you have any questions.
Forgive for my poor English.
models.py
from django.db import models
from django.urls import reverse
from taggit.managers import TaggableManager
class KnowHow(models.Model):
BASIC_TAGS =(
('1','one'),
('2','two'),
('3','three'),
('4','four'),
('5','five'),
('6','six'),
)
CATEGORY =(
('1','Type2'),
('2','Type1'),
)
author = models.ForeignKey('auth.User',on_delete=models.CASCADE)
category = models.CharField(max_length=1,choices=CATEGORY,default='1')
title = models.CharField(max_length=200)
text = models.TextField(blank=True,default=' ')
# delault=' ':import system will give a error if text column is null
file = models.FileField(blank=True,upload_to='explicit_knowhows')
basic_tag = models.CharField(max_length=1,choices=BASIC_TAGS,default='1')
free_tags = TaggableManager(blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('portal:index')
class Feedback(models.Model):
EFFECT =(
('1','great'),
('2','maybe good'),
('3','bad'),
)
NOVEL =(
('1','I didn't know that'),
('2','I know, but I forgot'),
('3','I know this.'),
)
kh = models.ForeignKey(KnowHow, on_delete=models.PROTECT)
user = models.ForeignKey('auth.User',on_delete=models.CASCADE)
query = models.TextField(blank=True)
time = models.DateTimeField(auto_now_add=True)
efficacy = models.CharField(max_length=1,choices=EFFECT,default='1')
novelty = models.CharField(max_length=1,choices=NOVEL,default='1')
def __str__(self):
return self.time.strftime("%Y/%m/%d %H:%M:%S")
views.py
from django.urls import reverse, reverse_lazy
from django.http import HttpResponse
from django.views import generic
from django.views.generic.edit import ModelFormMixin
from django.shortcuts import redirect,get_object_or_404
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from .models import KnowHow
from taggit.models import Tag
from .forms import SearchForm,FeedbackForm
from django.db.models import Q
"""
Django Auth
The LoginRequired mixin
https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-loginrequired-mixin
The login_required decorator
https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-login-required-decorator
#login_required
"""
class IndexView(LoginRequiredMixin,generic.list.ListView):
model = KnowHow
#paginate_by = 5
ordering = ['-title']
# template_name = 'portal/KnowHow_list.html'
class DetailView(ModelFormMixin,LoginRequiredMixin,generic.detail.DetailView):
# from https://torina.top/detail/337/
model = KnowHow
form_class = FeedbackForm
template_name = 'portal/KnowHow_detail.html'
def form_valid(self, form):
kh_pk = self.kwargs['pk']
Feedback = form.save(commit=False)
Feedback.kh = get_object_or_404(KnowHow, pk=kh_pk)
Feedback.query=""
Feedback.user=self.request.user
Feedback.save()
return redirect('portal:search')
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
self.object = self.get_object()
return self.form_invalid(form)
class CreateView(LoginRequiredMixin, generic.edit.CreateView): # The LoginRequired mixin
model = KnowHow
fields = ['category','title','text','file','basic_tag','free_tags']
#template_name = 'portal/KnowHow_form.html'
def form_valid(self, form):
# This method is called when valid form data has been posted.
# It should return an HttpResponse.
# https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user
form.instance.author = self.request.user
return super(CreateView, self).form_valid(form)
class UpdateView(LoginRequiredMixin, generic.edit.UpdateView): # The LoginRequired mixin
model = KnowHow
fields = ['category','title','text','file','basic_tag','free_tags']
#template_name = 'portal/KnowHow_form.html'
class DeleteView(LoginRequiredMixin, generic.edit.DeleteView): # The LoginRequired mixin
model = KnowHow
success_url = reverse_lazy('portal:index')
def delete(self, request, *args, **kwargs):
result = super().delete(request, *args, **kwargs)
Tag.objects.filter(knowhow=None).delete()
return result
#template_name = 'portal/KnowHow_confirm_delete.html'
class SearchIndexView(LoginRequiredMixin, generic.ListView):
template_name="search/search_index.html"
model = KnowHow
def post(self, request, *args, **kwargs):
form_value = [
self.request.POST.get('basic_tag', None),
self.request.POST.get('free_tags', None),
]
request.session['form_value'] = form_value
self.request.GET = self.request.GET.copy()
self.request.GET.clear()
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
basic_tag = ''
free_tags = ''
if 'form_value' in self.request.session:
form_value = self.request.session['form_value']
basic_tag = form_value[0]
free_tags = form_value[1]
default_data = {'basic_tag': basic_tag,
'free_tags': free_tags,
}
test_form = SearchForm(initial=default_data)
context['test_form'] = test_form
return context
def get_queryset(self):
if 'form_value' in self.request.session:
form_value = self.request.session['form_value']
basic_tag = form_value[0]
free_tags = form_value[1]
condition_basic_tag = Q()
condition_free_tags = Q()
if len(basic_tag) != 0 and basic_tag[0]:
condition_basic_tag = Q(basic_tag=basic_tag)
if len(free_tags) != 0 and free_tags[0]:
condition_free_tags = Q(free_tags__name__in=free_tags)
return KnowHow.objects.filter(condition_basic_tag & condition_free_tags).distinct()
else:
return KnowHow.objects.none()
#login_required
def help(request):
return HttpResponse("Member Only Help Page")
urls.py
from django.urls import path
from . import views
# set the application namespace
# https://docs.djangoproject.com/en/2.0/intro/tutorial03/
app_name = 'portal'
urlpatterns = [
# ex: /
path('', views.IndexView.as_view(), name='index'),
# ex: /KnowHow/create/
path('KnowHow/create/', views.CreateView.as_view(), name='create'),
# ex: /KnowHow/1/
path('KnowHow/<int:pk>/detail/', views.DetailView.as_view(), name='detail'),
# ex: /KnowHow/1/update/
path('KnowHow/<int:pk>/update/', views.UpdateView.as_view(), name='update'),
# ex: /KnowHow/1/delete
path('KnowHow/<int:pk>/delete/', views.DeleteView.as_view(), name='delete'),
# ex: /KnowHow/help/
path('KnowHow/help/', views.help, name='help'),
path('search/',views.SearchIndexView.as_view(), name='search')
]
There are several solutions for your problem.
First one is the exact solution you mentioned yourself. using a query string parameter like ?q= for KnowHow details view.
Using a SearchLog model and using that model's identifier. When someone hits the /search/ endpoint, you create a new SearchLog and pass the pk for this record to your front. Basically it would be just like ?q= option. instead you can use ?search_id= to bind the feedback to an specific SearchLog
Use user sessions. Bind the searched query to user's session and when they want to create a new Feedback use the query in their session.
For the first two options, you just need to create your urls for the detail links properly (in your search result page). In your template, do something like below:
# You are probably doing something like this
{% for r in results %}
{{r.name}}
{% endfor %}
# You should do this instead
{% for r in results %}
{{r.name}}
{% endfor %}
You can either pass the current_query in your context when rendering the template, or use javascript to get that value from browser's location / query string.
I changed get_context_data function in SearchIndexView to this:
in the last line before return add these two lines
context['basic_tag'] = basic_tag
context['free_tags'] = free_tags
And I changed html too.
{{ KnowHow.title }}
Thanks, #n1ma

How to include an object from another model in a generic Django CreateView?

Ok so I am trying to include the corrosponding Comment in my createAnswer View currently the url of the createAnswer page includes the pk ok the right comment so i need to get the comment by the id in the url.
My generic CreateView looks like this:
class createAnswer(CreateView):
model = Answer
fields = ['content']
def getComment(self, request):
???
comment = getComment()
def get_success_url(self):
this_path = self.request.get_full_path()
path_list = this_path.split('/')
def get_comment_id(self):
for i in range(len(path_list)):
if path_list[i].isdigit():
return path_list[i]
return '/blogcontact/comment/'+ get_comment_id(self)
def form_valid(self,form):
this_path = self.request.get_full_path()
path_list = this_path.split('/')
def get_comment_id(self):
for i in range(len(path_list)):
if path_list[i].isdigit():
return path_list[i]
form.instance.author = self.request.user
form.instance.comment = Comment.objects.get(id=get_comment_id(self))
return super().form_valid(form)
My Urls.py looks like this:
from django.urls import path
from . import views
from .views import createAnswer
urlpatterns = [
path('contact/comment/<int:pk>/newanswer', createAnswer.as_view(),
name='answer-create')
]<br>
I would like to save the Comment object in a variable so i Can use it in the html template like this {{comment}}
I think you you are confusing the function views and the Class Based Views (CBV), and you never import a request, it is just a parameter your views receive.
In a function view you do the following:
def my_view(request):
if request.method == 'POST':
# do some stuff
For CBV each method is a function:
from django.views.generic.edit import CreateView
class MyView(CreateView):
model = Answer
fields = ['content']
def get(self, request):
# do some stuff
def post(self, request):
# do some stuff
EDIT: To access the url parameters in class based views use self.kwargs, so you would access the comment pk by doing self.kwargs['pk']. Now you just need to get the comment and add it to the context data:
class CreateAnswer(CreateView):
model = Answer
fields = ['content']
def get_context_data(self, **kwargs):
kwargs['comment'] = Comment.objects.get(pk=self.kwargs['pk'])
return super().get_context_data(**kwargs)
def form_valid(self, form):
# do some stuff

Pass context data from generic.DetailView

How can i pass the context data which is coming from a forms.py to my views class which is using generic detailView, i need to pass forms.py to my product detail page.
Here is the code for my view class
class ProductView(generic.DetailView):
model = Product
cart_product_form = CartAddProductForm()
context = {'cart_product_form': cart_product_form}
template_name = 'shopping/product.html'
query_pk_and_slug = True
Please let me know if this is incorrect
Override get_context_data, and add the form to the context before returning it.
class ProductView(generic.DetailView):
model = Product
template_name = 'shopping/product.html'
query_pk_and_slug = True
def get_context_data(self, **kwargs):
context = super(ProductView, self).get_context_data(**kwargs)
cart_product_form = CartAddProductForm()
context['cart_product_form'] = cart_product_form
return context

Django-haystack generic SearchView - no results

I'm tryinig to get haystack working with a class-based generic view according to the documentation here. I can get results from a SearchQuerySet in the shell, so the models are being indexed. But I can't get the view to return a result on the page.
The main reason for using the generic view is that I want to extend later with more SQS logic.
I'm probably missing something obvious...
views.py :
from haystack.query import SearchQuerySet
from haystack.generic_views import SearchView
from .forms import ProviderSearchForm
from .models import Provider
class ProviderSearchView(SearchView):
template_name = 'search/provider_search.html'
form_class = ProviderSearchForm
def get_context_data(self, *args, **kwargs):
""" Extends context to include data for services."""
context = super(ProviderSearchView, self).get_context_data(*args, **kwargs)
context['body_attr'] = 'id="provider-search"'
return context
def get_queryset(self):
queryset = super(ProviderSearchView, self).get_queryset()
return queryset.filter(is_active=True)
search_indexes.py:
from haystack import indexes
from .models import Provider
class ProviderIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
title = indexes.CharField(model_attr='name')
created = indexes.DateTimeField(model_attr='created')
def get_model(self):
return Provider
def index_queryset(self, using=None):
"Used when the entire index for model is updated."
return self.get_model().objects.all()
forms.py
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Submit
from crispy_forms.bootstrap import FieldWithButtons
from haystack.forms import SearchForm
from .models import Provider
class ProviderSearchForm(SearchForm):
""" Override the form with crispy styles """
models = [ Provider ]
def __init__(self, *args, **kwargs):
super(ProviderSearchForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.disable_csrf = True
self.helper.form_tag = False
self.helper.form_show_labels = False
self.helper.layout = Layout (
FieldWithButtons(
Field('q', css_class='form-control input-lg', placeholder="Search providers..."),
Submit('','Search', css_class='btn btn-lg btn-primary'))
)
def get_models(self):
return self.models
def search(self):
sqs = super(ProviderSearchForm, self).search().models(*self.get_models())
return sqs
def no_query_found(self):
return self.searchqueryset.all()
The problem was that my page template was using the wrong variable in the for loop.
The documentation suggests:
for result in page_object.object_list
It should be:
for result in page_obj.object_list
note the template variable is page_obj.
See issue post on GitHub

Categories