How can I include a CreateView template in a ListView? - python

I want to make my blog like Facebook, where the option to create a post is in the same view as the list of posts.
I'm trying to include the CreateView template in my ListView, but it gives me this error:
Parameter "form" should contain a valid Django Form.
I also tried to use the same method in comments. Does this method work or is there another way?
This is the post_list.html template:
{% extends "posts/post_base.html" %}
{% load bootstrap4 %}
{% block post_content %}
<div class="col-md-6">
{% for post in post_list %}
{% include "posts/_post.html" %}
{% inclide 'posts/_create_post.html %}
{% endfor %}
</div>
{% endblock %}
This is the create_post.html template:
{% load bootstrap4 %}
<form method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" name="" value="comment">
</form>
What I want to do is just include the create template inside list template. If this will not work, I just want to let the user create a new post on the same page as the list of posts like Facebook.

Within your ListView you need to add the form
def get_context_data(self, *args, **kwargs):
ctx = super().get_context_data(*args, **kwargs)
ctx['form'] = myForm(self.request.POST or None)
return ctx
Then you should be able to use in the template

Related

Django display a variable in another template

I have a django project, where I have two templates: notification.html and base.html. Using get_context_data() method I display the number of answers at the notification.html. I tried to use the same variable in base.html, but it didn't work. I also created CBV that and used get_context_data() to pass the same logic and display it at the base.html, but it doesn't work. How do I display the variable in another template?
I don't how to pass 'answers.count' to the base.html, if base.html doesn't have a view (I created this only now trying to display this variable).
forum.views
class NotificationView(ListView):
model = Answer
template_name = 'forum/notification.html'
# If user visited the notification page - mark all answers as read
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
question = Question.objects.filter(user=user)
answer_read = self.model.objects.filter(question__in=question)
answer_read.update(is_read=True)
return answer_read
def get_context_data(self, **kwargs):
context = super().get_context_data()
user = get_object_or_404(User, username=self.kwargs.get('username'))
question = Question.objects.filter(user=user)
context['answers_all'] = Answer.objects.filter(question__in=question).order_by('-date_posted')
context['answers'] = Answer.objects.filter(question__in=question, is_read=False)
return context
forum/notification.html
{% extends 'main/base.html' %}
{% block content %}
<div class="container">
<b>УNotofocations: {{ answers.count }}</b>
{% for answer in answers_all %}
<div class="card mb-3 mt-2">
<div class="card-body">
Ответ на вопрос: {{ answer.question }}
<p>{{ answer.detail }}</p>
{{ answer.user.username }}
<span class="ml-4">{{ answer.date_posted|naturalday }}</span>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
main.views
class BaseView(TemplateView):
template_name = 'main/base.html'
def get_context_data(self, **kwargs):
context = super().get_context_data()
user = get_object_or_404(User, username=self.kwargs.get('username'))
question = Question.objects.filter(user=user)
context['answers'] = Answer.objects.filter(question__in=question)
return context
main/base.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
{% if user.is_authenticated %}
{% if answers.count < 1 %}
<i class="fas fa-bell"></i>
{% else %}
<i class="fas fa-bell"></i><span class="pending">{{ answers.count }}</span>
{% endif %}
{% endif %}
</head>
</html>
guess you are only missing the build-in template tags
in forum/notification.html
{% extends "base.html" %}
in main/base.html
{% block content %}{% endblock %}
https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#extends
https://docs.djangoproject.com/en/4.1/ref/templates/language/#template-inheritance
Finally, I solved this problem. The idea was to display the variable from context_data at base.html. As get_context_data didn't help, I found the solution in Django documentation. The solution was so easy, I hope to save time for other learners if they got the same problem.
I used context_processors.py to pass extra information and display it on the page. My step were:
Created a context_processors.py file in my app directory.
Created a function def get_notifications_number(request) where I filter what I want to display at base.html and returned return {'answers': answers}
Finally, I registered the file in Django TEMPLATES 'my_app.context_processors.get_notifications_number'.

How to render multiple forms in a single page using Material Design for django forms?

How can we display two forms in a single page using the Material Design for django forms?
In the forms.py, we can add code like
template = Template("""
{% form %}
{% part form.username prefix %}<i class="material-icons prefix">account_box</i>{% endpart %}
{% part form.email prefix %}<i class="material-icons prefix">email</i>{% endpart %}
{% part form.password prefix %}<i class="material-icons prefix">lock_open</i>{% endpart %}
{% endform %}
""")
And then in the template we have
{% block formbody %}
{% include form.template %}
{% endblock %}
How we can add one more form to the page say form2?
This is explained in the documentation:
Options:
form - form instance to render, if not specified, takes from form context variable
(emphasis mine)
So to render a different form:
{% form form=login_modal %}
form code here
{% endform %}
This means that in your view, you need to provide the context variable bound to the form:
from viewflow.flow.views import PerformTaskView
from .forms import LoginForm
class MyView(PerformTaskView):
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['login_modal'] = LoginForm()
return context

Upload CSV file in django admin list view, replacing add object button

I want to replace the add object button in the listview of an admin page. The underlying idea is that an administrator can download data on all models in the db, use a tool to edit the data, and then reupload as a CSV file.
In the list view I am struggling to override the form, as setting
class SomeModelForm(forms.Form):
csv_file = forms.FileField(required=False, label="please select a file")
class Meta:
model = MyModel
fields = '__all__'
class SomeModel(admin.ModelAdmin):
change_list_template = 'admin/my_app/somemodel/change_list.html'
form = SomeModelForm
other stuff
The admin change_list.html is overridden as follows:
{% extends "admin/change_list.html" %}
{% load i18n admin_urls admin_static admin_list %}
{% block object-tools-items %}
<form action="{% url 'admin:custom_submit_row' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{{ form.as_p }}
</p>
<p><input type="submit" value="Upload" /><input type="reset" value="Reset"></p>
</form>
{% endblock %}
Previously SomeModel was missing the class Meta, as per sebbs response this is updated. The original error has been resolved but now currently the admin page is displaying the upload and reset buttons but no field for file uploads.
cheers
Edited with sebb's input below. Thanks sebb.
The error fixed was
< class ‘my_model.admin.SomeModelAdmin'>: (admin.E016) The value of 'form' must inherit from 'BaseModelForm'
OP here, solution is as follows:
class SomeModelForm(forms.Form):
csv_file = forms.FileField(required=False, label="please select a file")
class SomeModel(admin.ModelAdmin):
change_list_template = 'admin/my_app/somemodel/change_list.html'
def get_urls(self):
urls = super().get_urls()
my_urls = patterns("",
url(r"^upload_csv/$", self.upload_csv, name='upload_csv')
)
return my_urls + urls
urls = property(get_urls)
def changelist_view(self, *args, **kwargs):
view = super().changelist_view(*args, **kwargs)
view.context_data['submit_csv_form'] = SomeModelForm
return view
def upload_csv(self, request):
if request.method == 'POST':
form = MineDifficultyResourceForm(request.POST, request.FILES)
if form.is_valid():
# process form
with the template overridden as so:
{% extends "admin/change_list.html" %}
{% load i18n admin_urls admin_static admin_list %}
{% block object-tools %}
{% if has_add_permission %}
<div>
<ul class="object-tools">
{% block object-tools-items %}
<form id="upload-csv-form" action="{% url 'admin:upload_csv' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ submit_csv_form.as_p }}</p>
<p>{{ submit_csv_form.csv_file.errors }}</p>
<p><input type="submit" value="Upload" />
<input type="reset" value="Reset"></p>
</form>
{% endblock %}
</ul>
</div>
{% endif %}
{% endblock %}
The form needs some custom validation but otherwise this solves the difficult part of customizing the admin page.
To elaborate what is going on here:
get_urls is overridden so that an additional endpoint can be added to the admin page, this can point to any view, in this case it points upload_csv
changelist_view is overridden to append the form info to the view
the change_list.html template block "object-tools" is overridden with the form fields
Hopefully someone else finds this helpful as well.
to your class SomeModelForm add something like this:
class Meta:
model = YourModel
fields = '__all__'
and change from forms.Form to forms.ModelForm

CSS for Flask wtforms

I have a the following code for a Form that I have in my Flask application using Wtforms. I use FieldList to use two fields for one part.
class A(Form)
additional = FieldList(FormField(Additional), 'Additional', min_entries=1)
submit = SubmitField('Submit')
class Additional(Form):
choices = [('Funding Mechanism', 'Funding Mechanism'), ('Study Section Name', 'Study Section Name')]
critera = SelectField('Additional Criteria', choices=choices)
input = StringField()
The template uses wtf.quick_form:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Grants - Find Grant{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Specify</h1>
</div>
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
{% endblock %}
Currently the forms render in a squished and overlapping way like so:
How can I change the code so that it is formated in one line like below? It is a screenshot of #Niklas in Stockholm 's form from his question.
Thank you!
Since your form class A is calling class Additional as a FormField and only adding submit to the field, i added the submit button the Additional form itself and then called it in the view.
In the template, use
{{ wtf.quick_form(form, form_type="inline") }}
It outputs the page like this:
The form_type argument adds the .form-inline to the class attribute.
This is just a hack, surely your form will have more inputs than this, for that, you'll be writing the whole form template yourself.
The issue is that {{ wtf.quick_form(form) }} is calling wtf.form_field() on your FieldList additional in A instead of calling it on additional's subfields. Because of this, I don't think you will be able to use wtf.quick_form() on your particular form.
Instead, try templating your form like this:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Grants - Find Grant{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Specify</h1>
</div>
<div class="col-md-4">
<form class="form form-horizontal" method="post" role="form">
{{ form.hidden_tag() }}
{{ wtf.form_errors(form, hiddens="only") }}
{% for subfield in form.additional %}
{{ wtf.form_field(subfield) }}
{% endfor %}
{{ wtf.form_field(form.submit) }}
</form>
</div>
{% endblock %}
You can read more about wtf.form_field() on the Flask-Bootstrap documentation site.

Django redirect back to previous page on form post

I'm trying to have a form post redirect back to the page it was posted from. So my app has a detail page that displays information about a place, /arena/123, and on the same page there is a form that a user can enter some comments or ratings about the current place they are looking at. These are my view functions.
class DetailView(LoginRequiredMixin, generic.DetailView):
model = Arena
template_name = 'arenas/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
if 'form' not in context:
print 'no form in context'
context['form'] = RatingForm
return context
def rate(request):
if request.method == 'POST':
form = RatingForm(request.POST)
if form.is_valid():
# post was valid. go back to page and display data
return DetailView.as_view()(request(), form=RatingForm)
else:
# post was unsuccessful. go back to the page and display errors
print form.errors
return DetailView.as_view()(request(), form=form)
and my template
{% extends "base.html" %}
{% block content %}
<h1>{{ arena.address }}</h1>
<h1>{{ arena.address_2 }}</h1>
<h1>{{ arena.phone }}</h1>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<form action="/arenas/rate/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
<div>
{{comments}}
</div>
{% endblock %}
So what I'd like to do is after a user submits the form on the page I'd like to go back to the same detail page I was on and either have the data that user posted there shown in the comments section if it was successful or redirect back to the same page but with the form errors if it was unsuccessful. My problem is right now I don't know how to retain the url and form errors to display the detail page again. I don't think I can use return DetailView.as_view()(context(), form=form) because my DetailView doesn't have a post so I get a 405. Every example I see doesn't cover redirecting back to the same page. They usually just show a HttpResponseRedirect(/thanks) or something similar.
Also note. I don't want to post to /arena/123 because eventually that will be another feature to update information about the place so I didn't want to put the comments post on that url.
So I dug into some more Django examples and stumbled across SingleObjectMixin. This combined with a FormView seems to be what I want. The Django docs had an example that was 99.9 percent what I wanted here. Just be sure to read down to the better solution section.
I changed my views to this
class DetailView(LoginRequiredMixin, generic.DetailView):
model = Arena
template_name = 'arenas/detail.html'
def get_context_data(self, **kwargs):
print kwargs
context = super(DetailView, self).get_context_data(**kwargs)
context['form'] = RatingForm
return context
class RatingView(LoginRequiredMixin, detail.SingleObjectMixin, generic.FormView):
model = Arena
template_name = 'arenas/detail.html'
form_class = RatingForm
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(RatingView, self).post(request, *args, **kwargs)
def get_success_url(self):
print "successfully posted"
return reverse('arenas:detail', kwargs={'pk': self.object.pk})
Added a route for posting the form with /rate
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$',
views.DetailView.as_view(), name='detail'),
url(r'^(?P<pk>\d+)/rate/$', views.RatingView.as_view(), name='rate')
)
and modified my template a bit to pass the id of the object to my route in the form post action
{% extends "base.html" %}
{% block content %}
<h1>{{ arena.address }}</h1>
<h1>{{ arena.address_2 }}</h1>
<h1>{{ arena.phone }}</h1>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<form action="/arenas/{{ arena.id }}/rate/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
<div>
{{comments}}
</div>
{% endblock %}
Now on an error the context is kept and the SingleObjectMixin/FormView combination keeps me on the same page to display the form errors and on a successful post it redirects to the detail page I want using the get_success_url to load the page again with the new information that was posted.

Categories