Formset in Django Classbased CreateView - python

Model
class Timetable(models.Model):
day = models.CharField(max_length=9,choices=timetable_choices)
start = models.IntegerField()
end = models.IntegerField()
period = models.CharField(max_length=12)
Views
class Timetableadding(CreateView):
model = Timetable
fields = ['day','period','start' ,'end']
success_url = '/dashboard'
What I need is to process a view similar to following image ,
NB: I am not good in js so i want a solution without the use of JS

Views
class Timetableadding(CreateView):
model = Timetable
success_url = '/dashboard/'
form_class = Timetableform
template_name = 'form.html'
def get_context_data(self, **kwargs):
context = super(Timetableadding, self).get_context_data(**kwargs)
context['formset'] = TimetableFormSet(queryset=Timetable.objects.none())
context['day_form'] = DayForm()
return context
def post(self, request, *args, **kwargs):
formset = TimetableFormSet(request.POST)
day_form = DayForm(data=request.POST)
if formset.is_valid() and day_form.is_valid():
return self.form_valid(formset,day_form)
def form_valid(self, formset,day_form):
day = day_form.cleaned_data['day']
instances = formset.save(commit=False)
for instance in instances:
instance.day = day
instance.save()
return HttpResponseRedirect('/dashboard/')
Forms
class DayForm(Form):
day = ModelChoiceField(queryset=Day.objects.all())
class Timetableform(ModelForm):
class Meta:
model = Timetable
fields = ( 'day','start', 'end', 'period')
TimetableFormSet = modelformset_factory(Timetable, fields=('start', 'end', 'period'),extra=8,)
Template
{% csrf_token %}
{{ day_form }} <br>
{{ formset.management_form }}
{% for form in formset %}
{{ form }}<br><br>
{% endfor %}

CREATE FORMS.PY
class MyForm(ModelForm):
class Meta:
model = Timetable
fields = ['day','start','end','period',]
Views.py
from django.forms import formset_factory
class YourView(CreateView):
form = formset_factory(MyForm)
model = Timetable
success_url ="Your success url"
template_name = "your template"
In Your Templates
<form method="post">{% csrf_token %}
<fieldset>
<div class="row">
<div class="form-group col-lg-12">
{{ form.management_form }}
{% for contact in form %}
<div class="link-formset">
{{ contact.as_p }}
</div>
{% endfor %}
<button class="user-sent" type="submit" value="Send"> Send</button>
</div>
</div>
</fieldset>
<script src="{% static 'forms/jquery.formset.js' %}"></script>
<script>
$('.link-formset').formset({
addText: '<i class="fa fa-plus"></i> Add User',
deleteText: '<i class="fa fa-trash-o"></i>Remove'
});
</script>
</form>

Related

Django search / filter across multiple models

In my Django website, I'm trying to create a search bar on my homepage that returns results that are stored in two different models(Articles, Jobs)
Currently, I get an empty result set when I search using this code:
In models.py,
class Articles(models.Model):
objects = None
news_title = models.CharField(db_column="title", max_length=400)
news_url = models.URLField(db_column="link", max_length=400)
image_link = models.CharField(db_column="image", max_length=400)
published_date = models.CharField(db_column="published_date", max_length=400)
class Meta:
managed = False
db_table = "articles"
def __str__(self):
return self.news_title
class Jobs(models.Model):
objects = None
company = models.CharField(db_column='company', max_length=100)
job_title = models.CharField(db_column='job_title', max_length=300)
experience = models.CharField(db_column='experience', max_length=300)
edu_level = models.CharField(db_column='edu_level', max_length=50)
class Meta:
managed = False
db_table = "job_list"
def __str__(self):
return self.job_title
In views.py,
class SearchView(ListView):
template_name = 'blog/search_results.html'
def get_queryset(self):
request = self.request
query = request.GET.get('q', '')
articles_results = Articles.objects.filter(Q(news_title__icontains=query))
jobs_results = Jobs.objects.filter(Q(job_title__icontains=query))
context={
'articles':articles_results,
'jobs':jobs_results,
}
return render(request, 'blog/search_results.html', context)
In main_view.html, I'm using this code for creating a search bar:
<form action="{%url 'search_results' %}" method="get" values="{{request.GET.q}}" class="search-jobs-form">
<div class="row mb-5">
<input name="q" type="text" values="{{request.GET.q}}" placeholder="search">
</div>
<button type="submit">Search</button>
</form>
And in search_results.html,
{% block content %}
{% for job in jobs %}
<h5>{{job.job_title}}</h5>
<p>{{job.company}}</p>
{% endfor %}
{% for article in articles %}
<h5>{{article.news_title}}</h5>
<p>{{article.published_date}}</p>
{% endfor %}
{% endblock %}
I ended up not using class SearchView(ListView) in views.py and made it work
Here's the code that I used
#index.html Search Bar
<form method="get" class="search-jobs-form" action="{% url 'main_search'%}">
{% csrf_token %}
<input type="text" placeholder="Job title, Company..." name="search_query">
<button type="submit" class="btn btn-info btn-lg btn-block text-white btn-search">Search Job</button>
</form>
In views,
#views.py
def main_search(request):
q = request.GET['search_query']
articles = Articles.objects.filter(news_title__icontains=q)
jobs = Jobs.objects.filter(Q(job_title__icontains=q) | Q(company__icontains=q))
return render(request, "blog/main_search.html", {'articles':articles, 'jobs':jobs, 'page_name':'Search Results', 'q':q})

Wrong url in pagination. Django

I want to search by two fields (q1 and q2):
home.html
<form action="{% url 'search_results' %}" method="get">
<input name="q" type="text" placeholder="Search...">
<select name="q2" class="form-control" id="exampleFormControlSelect1">
<option>All locations</option>
<option>RU</option>
<option>Ukraine</option>
<option>USA</option>
</select>
<button> Search </button>
</form>
When I click "search" I go to http://127.0.0.1:8001/search/?q=mos&q2=RU (it's OK)
Then I click "next". I go to
http://127.0.0.1:8001/search/?city=2&q=mos&q2=%20RU (ERROR: "Page not found)
but I want
http://127.0.0.1:8001/search/?city=2&q=mos&q2=RU
How can I fix it? Why do I have "%20" ???
search results.html
<h1>Search Results</h1>
<ul>
{% for city in object_list %}
<li>
{{ city.name }}, {{ city.state }}
</li>
{% endfor %}
</ul>
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
previous
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>
models.py
from django.db import models
class City(models.Model):
name = models.CharField(max_length=255)
state = models.CharField(max_length=255)
COUNTRY = (
('RU', 'Russia'),
('UKR', 'Ukraine'),
('US', 'USA'),
)
category = models.CharField(max_length=100, choices=COUNTRY, default='RU')
class Meta:
verbose_name_plural = "cities"
def __str__(self):
return self.name
urls.py
urlpatterns = [
path('search/', SearchResultsView.as_view(), name='search_results'),
path('', HomePageView.as_view(), name='home'),
path('city/<int:pk>/', views.city_detail, name='city_detail'),
]
views.py
class HomePageView(ListView):
model = City
template_name = 'cities/home.html'
paginate_by = 3
page_kwarg = 'city'
def city_detail(request, pk):
city = get_object_or_404(City, pk=pk)
return render(request, 'cities/city_detail.html', {'city': city})
class SearchResultsView(ListView):
model = City
template_name = 'cities/search_results.html'
paginate_by = 3
page_kwarg = 'city'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['query'] = self.request.GET.get('q')
# added param
context['query2'] = self.request.GET.get('q2')
#print("BBBBBBBBBBBBBBBBb {}".format(context['query2']))
return context
def get_queryset(self): # new
query = self.request.GET.get('q')
query2 = self.request.GET.get('q2')
#print("AAAAAAAAAAAAAAAAAAAAA", query2, type(query2))
object_list = City.objects.filter(
(Q(name__icontains=query) | Q(state__icontains=query)) & Q(category=query2)
)
return object_list
%20 is the url code for a space. You need to remove the extra spaces in :
previous
# ^
and
next
# ^

Automatically set logged-in user as the author in django using createview and modelform

I am building a frontend form that allows someone to post an article without accessing the admin.
When the user is logged in, I would like for him/her to be able to write an article. Upon saving, I would like that user to automatically be set as the author of the article.
I am at an impasse. Any help would be much appreciated.
models.py
from django.db import models
from django.urls import reverse
from django.contrib.auth.models import User
from django.utils import timezone
class Article(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length=65)
text = HTMLField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
class ArticleImage(models.Model):
image = CloudinaryField('image')
image_name = models.CharField(max_length=55,
default='')
article = models.ForeignKey(Article)
def __str__(self):
return self.image_name
class ArticleTag(models.Model):
slug = models.SlugField(max_length=50,
unique=True)
article = models.ForeignKey(Article)
def __str__(self):
return self.slug
class ArticleCategory(models.Model):
slug = models.SlugField(max_length=20,
unique=True)
article = models.ForeignKey(Article)
def __str__(self):
return self.slug
forms.py
class ArticleCreationForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'text']
widgets = {
'title': forms.TextInput(attrs={'placeholder': 'Please add a title. Max: 65 characters'}),
'text': forms.Textarea(attrs={'cols': 80, 'rows': 40, 'placeholder': 'Starting typing your article...'})
}
ArticleImageFormSet = inlineformset_factory(Article, ArticleImage,
fields=('image', 'image_name',),
extra=1,
max_num=1,
widgets={'image_name':
forms.TextInput(attrs={'placeholder': 'Image name'})})
ArticleTagFormSet = inlineformset_factory(Article, ArticleTag,
fields=('slug',),
extra=1,
max_num=1)
ArticleCategoryFormSet = inlineformset_factory(Article, ArticleCategory,
fields=('slug',),
extra=1,
max_num=1)
views.py
class CreateArticle(CreateView):
model = Article
form_class = ArticleCreationForm
template_name_suffix = '_add_form'
def get_success_url(self):
return reverse('accounts:detail', kwargs={'pk': self.object.pk})
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates blank versions of the form
and its inline formsets.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
articleimage_form = ArticleImageFormSet()
articletag_form = ArticleTagFormSet()
articlecategory_form = ArticleCategoryFormSet()
return self.render_to_response(
self.get_context_data(form=form,
articleimage_form=articleimage_form,
articletag_form=articletag_form,
articlecategory_form=articlecategory_form))
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
articleimage_form = ArticleImageFormSet(self.request.POST)
articletag_form = ArticleTagFormSet(self.request.POST)
articlecategory_form = ArticleCategoryFormSet(self.request.POST)
if (form.is_valid() and articleimage_form.is_valid() and
articletag_form.is_valid() and articlecategory_form.is_valid()):
return self.form_valid(form, articleimage_form, articletag_form,
articlecategory_form)
else:
return self.form_invalid(form, articleimage_form, articletag_form,
articlecategory_form)
def form_valid(self, form, articleimage_form, articletag_form,
articlecategory_form):
"""
Called if all forms are valid. Creates a Recipe instance along with
associated Ingredients and Instructions and then redirects to a
success page.
"""
self.object = form.save()
obj.author = request.user.username
articleimage_form.instance = self.object
articleimage_form.save()
articletag_form.instance = self.object
articletag_form.save()
articlecategory_form.instance = self.object
articlecategory_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, articleimage_form, articletag_form,
articlecategory_form):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(form=form,
articleimage_form=articleimage_form,
articletag_form=articletag_form,
articlecategory_form=articlecategory_form))
template.html
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<div class="row">
<div class="medium-9 columns">
{{ form.non_field_errors }}
<h2 class="article-identifier">Add a new article</h2>
<div class="fieldWrapper">
{{ form.title.errors }}
{{ form.title }}
<div id="title_feedback" class="text-right"></div>
</div>
<div class="fieldWrapper">
{{ form.text.errors }}
{{ form.text }}
</div>
</div>
<div class="medium-3 columns">
<div class="button-wrapper">
<input class="button" type="submit" value="Publish">
</div>
<fieldset class="image_upload">
<h2>Add an Image</h2>
{{ articleimage_form.management_form }}
{{ articleimage_form.non_form_errors }}
{% for form in articleimage_form %}
{{ form.id }}
<div class="inline {{ articleimage_form.prefix }}">
{{ form.image.errors }}
{{ form.image.label_tag }}
{{ form.image }}
{{ form.image_name.errors }}
{{ form.image_name.label_tag }}
{{ form.image_name }}
</div>
{% endfor %}
</fieldset>
<fieldset>
<h2>Add a Category</h2>
{{ articlecategory_form.management_form }}
{{ articlecategory_form.non_form_errors }}
{% for form in articlecategory_form %}
{{ form.id }}
<div class="inline {{ articlecategory_form.prefix }}">
{{ form.slug.errors }}
{{ form.slug.label_tag }}
{{ form.slug }}
</div>
{% endfor %}
</fieldset>
<hr />
<fieldset>
<h2>Add a Tag</h2>
{{ articletag_form.management_form }}
{{ articletag_form.non_form_errors }}
{% for form in articletag_form %}
{{ form.id }}
<div class="inline {{ articletag_form.prefix }}">
{{ form.slug.errors }}
{{ form.slug.label_tag }}
{{ form.slug }}
</div>
{% endfor %}
</fieldset>
</div>
</div>
</form>
Save the form with commit=False, set the user on the object, then save the object. Inside the form_valid method, you can access the user with self.request.user. You should assign the user instance, not the username as your code currently does.
obj = form.save(commit=False)
obj.author = self.request.user
...
obj.save
You should also restrict the view to logged in users. You can use the LoginRequiredMixin for this.
from django.contrib.auth.mixins import LoginRequiredMixin
class CreateArticle(LoginRequiredMixin, CreateView):
As per Django's documentation, you can just set form.instance.author to the current user (self.request.user), in the overriden form_valid method (you seem to have done something similar in your code already using other objects.). Then you can just return super().form_valid(form).
https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user
In your case you seem to have a need to do other things in your form_valid method, so it may not necessarily be correct for you to return super().form_valid(form).

UpdateView with Multiple Formset not working

I tried creating a generic UpdateView for a model with generic relations with 2 other models, based on the CreateView provided in this tutorial.
The form and formsets are populated as expected, but when I save I get redirected to the model detail page and changes are not saved.
My question is similar to this one but the suggestions provided are not helpful because they are specific to the former question.
Here is my code :
# models.py
class Ingredient(models.Model):
...
content_type = models.ForeignKey(ContentType, editable=False)
object_id = models.PositiveIntegerField(editable=False)
content_object = GenericForeignKey()
class Step(models.Model):
Same as Ingredient
class Recipe(models.Model):
...
ingredients = generic.GenericRelation(Ingredient)
steps = generic.GenericRelation(Step)
# forms.py
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
IngredientFormSet = generic_inlineformset_factory(Ingredient, extra=1)
StepFormSet = generic_inlineformset_factory(Step, extra=1)
# views.py
class RecipeUpdate(UpdateView):
model = Recipe
form_class = RecipeForm
template_name = 'app_recipes/recipe_add.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet(instance = self.object)
step_form = StepFormSet(instance = self.object)
return self.render_to_response(self.get_context_data(
form=form,
ingredient_form=ingredient_form,
step_form=step_form))
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet(self.request.POST, instance=self.object)
step_form = StepFormSet(self.request.POST, instance=self.object)
if (form.is_valid() and ingredient_form.is_valid() and step_form.is_valid()):
return self.form_valid(form, ingredient_form, step_form)
else:
return self.form_invalid(form, ingredient_form, step_form)
def form_valid(self, form, ingredient_form, step_form):
self.object = form.save()
ingredient_form.instance = self.object
ingredient_form.save()
step_form.instance = self.object
step_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, ingredient_form, step_form):
return self.render_to_response(self.get_context_data(
form=form,
ingredient_form=ingredient_form,
step_form=step_form))
def get_success_url(self):
return reverse('recipe_detail', kwargs={'pk': self.object.pk})
I am using the same template used by CreateView :
...
<script src="{{ STATIC_URL }}js/jquery.formset.js"></script> # django-dynamic-formset
<script type="text/javascript">
$(function() {
$(".inline.{{ ingredient_form.prefix }}").formset({
prefix: "{{ ingredient_form.prefix }}",
})
$(".inline.{{ step_form.prefix }}").formset({
prefix: "{{ step_form.prefix }}",
})
})
</script>
...
<form action="." method="post">
{% csrf_token %}
<div>
{{ form.as_p }}
</div>
<fieldset>
<legend>Recipe Ingredients</legend>
{{ ingredient_form.management_form }}
{{ ingredient_form.non_form_errors }}
{% for form in ingredient_form %}
{{ form.id }}
<div class="inline {{ ingredient_form.prefix }}">
{% for field in form.visible_fields %}
<div>
{{ field.errors }}
{{ field.label_tag }}
{{ field }}
</div>
{% endfor %}
</div>
{% endfor %}
</fieldset>
<fieldset>
<legend>Recipe Steps</legend>
{{ step_form.management_form }}
{{ step_form.non_form_errors }}
{% for form in step_form %}
{{ form.id }}
<div class="inline {{ step_form.prefix }}">
{% for field in form.visible_fields %}
<div>
{{ field.errors }}
{{ field.label_tag }}
{{ field }}
</div>
{% endfor %}
</div>
{% endfor %}
</fieldset>
<input type="submit" value="Add recipe" class="submit" />
</form>
...
NB : I use django-dynamic-formset in the template to dynamically add and remove forms in formsets.
Thank you

Using Check boxes in Taggit

I am trying to use Taggit for users to be able to tag their posts while they are submitting a form. But I can successfully let them type manually(their tags), I am trying to change to check boxes. Any ideas?
forms.py
class TalesForm(ModelForm):
class Meta:
model = Tale
fields = ('title', 'body', 'tags')
m_tags = TagField()
models.py
class Tale(models.Model):
title = models.CharField(max_length = 50)
body = models.TextField(max_length = 10000)
pub_date = models.DateTimeField(default = datetime.now)
poster = models.CharField(max_length = 30)
tags = TaggableManager()
def __unicode__(self):
return self.title
views.py
def add(request):
if request.user.is_authenticated():
if request.method=='POST':
form=TalesForm(request.POST)
if form.is_valid():
m_tags = form.cleaned_data['tags']
newTale=Tale(title=form.cleaned_data['title'], body=form.cleaned_data['body'], poster = request.user)
newTale.save()
for m_tag in m_tags:
newTale.tags.add(m_tag)
#form.save_m2m()
return HttpResponseRedirect('/home/%s'%newTale.id)
else:
return render_to_response('story/add.html', {'form':form}, context_instance=RequestContext(request))
else:
form=TalesForm()
args = {}
args.update(csrf(request))
args['form'] = form
context= {'form': form}
return render_to_response('story/add.html',context, context_instance=RequestContext(request))
else:
return HttpResponseRedirect('/home')
html
<form method="post" action="/home/add/">
{% csrf_token %}
<div class="register_div">
{% if form.title.errors %}<p class="error" >{{ form.title.errors }}</p>{% endif %}
<p><label for="title"{% if form.title.errors %} class="error"{% endif %}>Title:</label></p>
<p>{{ form.title}}</p>
</div>
<div class="register_div">
{% if form.body.errors %}<p class="error" >{{ form.body.errors }}</p>{% endif %}
<p><label for="body"{% if form.body.errors %} class="error"{% endif %}>Body:</label></p>
<p>{{ form.body }}</p>
</div>
<div class="register_div">
<p><label for="tag">Tags:</label></p>
<p>{{ form.tags }}</p>
</div>
<input type="submit" value="Add Story" name="submit" />
</form>
Define your form this way:
from taggit.models import Tag
class TalesForm(ModelForm):
tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all(),
widget=forms.CheckboxSelectMultiple())
class Meta:
model = Tale
fields = ('title', 'body', 'tags')

Categories