Help required, please.
I'm doing a level 6 Diploma in Software Development. Right now I'm doing a project that requires a gym app to be built.
We've decided on using Python/Django as our language/framework.
I have my models, urls, views, and templates working thus far. (Videos display and its own page)
However, I need to implement a search bar to search both video content and text context within the app.
I have the video model set up in /admin. migrations are done.
Do I need another model to search for normal content in my app?
The search bar is in my base.html template.
I want to use the search bar to do a query and pass the results onto a results template page
which extends base.html.
The error I get is the below;
Exception Type: ValueError
Exception Value: Cannot use None as a query value
Can someone point me in the right direction? photos of code attached.
base.html
<!-- Navbar Right Side -->
<div class="navbar-nav">
<form class="form-inline my-1 my-lg-0" action="{% url 'search_results' %}" method="get">
<input name="q" type="text" placeholder="Search" aria-label="Search">
<a class="btn btn-secondary my-2 my-sm-0" href='/gymapp/videos/' type=submit>Search</a>
</form>
<a class="nav-item nav-link" href="#">Login</a>
<a class="nav-item nav-link" href="/register">Register</a>
</div>
search_results.html
{% extends "gymapp/base.html" %}
{% load embed_video_tags %}
{% block content %}
{% for i in obj %}
{% video i.video 'tiny' %}
<ul class="list-unstyled mt-3 mb-4">
<li>Name: {{ i.video_name }}</li>
<li>Description: {{ i.content }}</li>
</ul>
{% endfor %}
{% endblock content %}
urls.py
path('videos/', VideoResultsView.as_view(), name='search_results'),
models.py
from django.db import models
from embed_video.fields import EmbedVideoField
class Video(models.Model):
category = models.CharField(max_length=200)
video_name = models.CharField(max_length=200)
content = models.CharField(max_length=500)
video = EmbedVideoField() # same like models.URLField()
stats = models.CharField(max_length=20)
class Meta:
verbose_name_plural = 'videos'
def __str__(self):
return self.category
views.py
class VideoResultsView(ListView):
model = Video
template_name = 'search_results.html'
def get_queryset(self):
query = self.request.GET.get('q')
video_list = Video.objects.filter(
Q(category__icontains=query) | Q(video_name__icontains=query)
)
return video_list
Hoping someone can point me in the right direction. Error Description
Found my answer.
<form class="form-inline my-1 my-lg-0" action="{% url 'search_results' %}" method="get">
<input name="q" type="text" placeholder="Search" aria-label="Search">
<a class="btn btn-secondary my-2 my-sm-0" href='/gymapp/videos/' type=submit>Search</a>
</form>
I shouldn't have used an tag for the button. Changed it to tag and now the query is working.
silly mistake.
Related
I am trying to display some extra context on the page, but when I adding get_context_data method it is displayed context but not a forms fields. This is because when I click ulr that triggers view below there is get method by default or prior to forms fields? I don't understand why forms disappear when this method present in SolutionCreate view, indeed all context data displayed
template
{% extends "base.html" %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-sm">
<form action="" method="POST">
<table>
{% csrf_token %}
{{ form.as_p }}
</table>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</div>
<div class="col-sm">
{{ context }}
<h5>Problem:</h5>
{% for pr in prb %}
<h5>Problem: {{ pr.title }}</h5>
<h6>Description</h6>
<li class="list-group-item">{{ pr.description }}</li>
<p>
</p>
<h6>Risks</h6>
<li class="list-group-item">{{ pr.risks }}</li>
<p>
</p>
<h6>Parts</h6>
<li class="list-group-item">{{ pr.parts }}</li>
<p>
</p>
<h6>Causes</h6>
<li class="list-group-item">{{ pr.causes }}</li>
<p>
</p>
<h6>Published</h6>
<li class="list-group-item">{{ pr.published }}</li>
<p>
</p>
<a href="{% url 'delete_problem' pr.pk %}"
class="btn btn-warning"
role="button"
aria-pressed="true">Delete</a>
<a href="{% url 'update_problem' pr.pk %}"
class="btn btn-warning"
role="button"
aria-pressed="true">Update</a>
<p>
</p>
{% endfor %}
</div>
</div>
</div>
{% endblock content %}
view
class SolutionCreate(CreateView):
model = Solution
template_name = 'analysis/create_solution.html'
fields = [
'problem',
'research',
'solutions',
'resources',
'plan',
'test'
]
def post(self, request, *args, **kwargs):
form = SolutionForm(request.POST)
if form.is_valid():
form.save(commit=True)
return HttpResponseRedirect('/saved/')
return render(request, self.template_name, {'form': form})
def get_context_data(self, *args, **kwargs):
prb = Problem.objects.select_related()
return {'prb': prb}
get_context_data is a method on the parent class. If you override it, you still need to call the parent method which is what adds the form to the context. You do this by calling super() inside your own method, to obtain the context data, and then add your own:
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['prb'] = Problem.objects.select_related()
return context
Refer to the documentation on adding extra context to see how you should use get_context_data.
This worked for me in a similar scenario
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(**kwargs)
prb = Problem.objects.select_related()
context.update({'prb': prb})
return context
This question has “grown” from Django Model Field for html5 Range Slider but is specific and different enough to warrant its own question, I believe, rather than continue in the comments section.
The aim of the original post was to write Django code that would render and display a slider in the webpage. That has been done. The challenge that this question relates to is writing the value of the slider to a field in a table/model. I will first show code for a table/model update that works then show how far I have got displaying the slider and showing where I am stuck. I am giving all this detail rather than just asking for a fix, because I am trying to understand how the Django code works - and by trying to explain it hopefully you can correct my misunderstandings.
First I will show the code I have copied and modified that just displays textboxes that the View(?) form(?) takes the input values and write a new record to the table. I will describe my understanding of the various parts of the view and the form.
Then I will show the code I have to display the slider and how it differs from the working code – which is where I need help to write data to the table/model.
My template, create.html, that works contains:
<form method="POST">
{% csrf_token %}
<div class="panel-body">
<div class="row">
<div class="col-lg-12">
<div class="form-group">
<label for="question">Enter Survey Question</label>
{% render_field form.q_text rows="3" class="form-control" %}
</div>
</div>
</div>
<div class="row">
<div class="col-lg-5">
<div class="form-group">
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
<label for="C1_type">Type 1</label>
{% render_field form.C1_Type class="form-control" %}
</div>
</div>
<div class="col-lg-5">
<div class="form-group">
<label for="option2">Choice 2</label>
{% render_field form.Choice2_text class="form-control" %}
<label for="C2_type">Type 2</label>
{% render_field form.C2_Type class="form-control" %}
</div>
</div>
</div>
<div class="row">
<hr />
<div class="col-lg-4">
<button type="submit" class="btn btn-info">Submit</button>
</div>
</div>
</div>
</form>
My understanding the form will be “interpreted” by the view as a POST.
The input fields which appear on the rendered webpage as input boxes, are coded in the template as in the following examples:
<label for="question">Enter Survey Question</label>
<label for="Choice1_text ">Choice 1</label>
<label for="C1_type">Type 1</label>
(There are other inputs)
Are “passed back” to the View(?) form(?) via the {% render_field
{% render_field form.q_text rows="3" class="form-control" %}
{% render_field form.Choice1_text class="form-control" %}
{% render_field form.C1_Type class="form-control" %}
There are other {% render_field
The
form.q_text
form.Choice1_text
form.C1_Type
are the names of the fields in the table/model to be updated by the data typed in the boxes on the webpage.
in forms.py, there is:
from django import forms
… deleted lines …
class AnyForm(forms.ModelForm):
class Meta:
model = CC_Questions
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
where:
class AnyForm(forms.ModelForm):
indicates this is a “modelForm” which is “used unmodified” in a view(?) template(?) will result in a collection of boxes to accept input being displayed on the screen, without any formatting.
the:
model = CC_Questions
is the name of the model/table to which data will be written
the:
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
Are the fields that will display input boxes in the template and will be the fields where the data is written in the model when data is input in the webpage. This form may not display all of the fields in the model if they do not all require a value from this form.
The view is:
def p2create(request):
if request.method == 'POST':
form = AnyForm(request.POST)
if form.is_valid():
form.save()
return redirect('/polls/p2')
else:
form = AnyForm()
context = {'form' : form}
return render(request, 'pollapp2/create.html', context)
Since the template contained the line
<form method="POST">
the code below
if request.method == 'POST':
will evaluate as True resulting in the form, AnyForm, being "used"
form = AnyForm(request.POST)
if the form is valid (how can a form be “invalid??)
if form.is_valid():
then save the values that the template “passes back” to the form (??) via the
{% render_field form.<field name>
The form “passes back” values to the view (??) via the
model =<model name>
fields = [“<field1>”, “<field2>”, ….]
and the view writes to the model (??) via the
form.save()
The next web page to be displayed is
return redirect('/polls/p2')
So that is my best understanding of how working code writes data to a table.
Now follows is the code I have working to display a slider, and the slider value, when I press the “Try it” button. The code successfully displays the value corresponding to the position of the slider on the underlying range. However, from the following I can’t work out what code I need to modify to “trap” the value of “myint” and write it to the appropriate field in a new record in the model.
Template that displays the slider:
<!-- p2slider.html -->
{% extends "pollapp2/base.html" %}
<!-- load widget tools to give me more control over layout of form in template -->
{% load widget_tweaks %}
{% block title %}P2Slider test{% endblock %}
{% block main %}
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">P2Slider test</h3>
</div>
<div class="panel-body">
{% render_field form.RangeInput class="SliderForm" %}
{{form.myint}}
<label for="myint">Input Value</label>
<!-- https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_range_value2 -->
<p>Click the button to display the value of the value attribute of the slider control.</p>
<button onclick="myFunction()">Try it</button>
<p id="slider_val"></p>
<script>
function myFunction() {
var x = document.getElementById("id_myint").value;
document.getElementById("slider_val").innerHTML = x;
}
</script>
<!-- end -->
</div>
<div class="panel-footer">
Data here
</div>
</div>
</div>
</div>
{% endblock %}
Form that “relates” to this template. I notice that this code doesn’t use ModelForm
# attempt at range input slider
#
from polls.widgets import RangeInput
#
class SliderForm(forms.Form):
myint = forms.IntegerField(widget=RangeInput)
View I have copied and modified from the view for “Create” but I guess is wrong because I don’t have a model form:
def p2slider(request):
if request.method == 'POST':
form = SliderForm(request.POST)
if form.is_valid():
form.save() # ??????
context = {'form' : form} # ????
return render(request,'pollapp2/p2slider.html',context)
else:
form = SliderForm()
context = {'form' : form} # ??????
return render(request,'pollapp2/p2slider.html', context)
So I’m guessing I need to change the view function p2slider to the relevant model, cc_resp_nofk. But I don’t know what to code.
This is a simple Form, so there is no .save() method (since it is unclear what that would mean). You can obtain the value with the form.cleaned_data:
def p2slider(request):
if request.method == 'POST':
form = SliderForm(request.POST)
if form.is_valid():
slider_value = form.cleaned_data['myint']
# do something …
else:
form = SliderForm()
context = {'form' : form}
return render(request,'pollapp2/p2slider.html', context)
or if you want to use it in a ModelForm, you can plug this in as the widget of a form field:
class SomeModelForm(forms.ModelForm):
class Meta:
model = SomeModel
fields = ['intfield']
widgets = {
'intfield': RangeInput
}
If SomeModel thus has an intfield as field, you can use the RangeInput as widget for that field, and then .save() that form.
I have this edit or update form in which I want to display only the image name in the form for a better user experience so that the user could know which image he has uploaded while creating the data.
I am storing the image name in the model as well,but i want to display the image name inside the image field.
forms.py
class MenuCategoryForm(forms.ModelForm):
image = forms.ImageField(allow_empty_file=True, required=False)
class Meta:
model = MenuCategory
fields = ['name', 'description', 'menu_options']
view
def menu_category_update(request, id):
item = MenuCategory.objects.get(id=id)
if request.method == 'POST':
form = MenuCategoryForm(request.POST, request.FILES, instance=item)
if form.is_valid():
if request.FILES['image']:
image = request.FILES['image']
image_url = upload_image(image, 'menu_category', image.name)
obj = form.save(commit=False)
obj.image_url = image_url
form.save()
else:
form.save()
return redirect('menu-category')
else:
form = MenuCategoryForm(instance=item)
context = {
'form': form,
'item': item
}
return render(request, 'menu/menu_category_update.html', context)
Template
{% extends 'partials/base.html' %} {% load crispy_forms_filters %} {% load
crispy_forms_tags %}
<!-- {% block title %}Menu category {% endblock %} -->
{% block content %}
<div class="container edit-form-flex">
<div class="row Edit-form-box">
<div class="form-inner-box bg-white">
<div class="heading-editing">
<h3>Edit menu category</h3>
</div>
<form method="POST" class="add-new-form edit-form" enctype="multipart/form-data">
{% csrf_token %} {{ form|crispy }}
<div class="update-buttons-container">
<button class="btn btn-info1" type="submit" value="Update">
Update
</button>
<a class="btn btn-secondary" href="{% url 'menu-category' %}"
>Cancel</a
>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
Well in other to get the name of the image in your form you will be better off in using a form initial like so:
def menu_category_update(request,id):
item = MenuCategory.objects.get(id=id)
if request.method == 'POST':
form = MenuCategoryForm(request.POST, request.FILES, instance=item)
if form.is_valid:
obj = form.save(commit=False)
# obj.username = request.user
form.save()
return redirect('menu-category')
else:
form = MenuCategoryForm(
initial={'image':item.image,
'name':item.name,
'description':iteem.description,
})# this are the fields that we want to show in the form
context = {
'form': form,
'item': item
}
return render(request, 'menu/menu_category_update.html', context)
In your form html you will apply the form initial to the field that you want to show in your form, by doing something like this: form.initial.name As i have illustrated below :
{% extends 'partials/base.html' %} {% load crispy_forms_filters %} {% load
crispy_forms_tags %}
<!-- {% block title %}Menu category {% endblock %} -->
{% block content %}
<div class="container edit-form-flex">
<div class="row Edit-form-box">
<div class="form-inner-box bg-white">
<div class="heading-editing">
<h3>Edit menu category</h3>
</div>
<form method="POST" class="add-new-form edit-form" enctype="multipart/form-data">
{% csrf_token %}
<label> Image name </label>
{{form.initial.name}}
<label> Description </label>
{{ form.initial.description }}
<label> Image </label>
{{ form.initial.image }}
<div class="update-buttons-container">
<button class="btn btn-info1" type="submit" value="Update">
Update
</button>
<a class="btn btn-secondary" href="{% url 'menu-category' %}"
>Cancel</a
>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
I am creating E-Learning website and I want to show "Course Content or Lession" as a playlist which is related to subject. Like that image
but I am getting error Cannot query "Python Tutorials Teaser": Must be "Subject" instance. Python Tutorials Teaser is title of the lession.
view.py
def allsubject(request):
subj = Subject.objects.all()
context = {'subj': subj}
return render(request, 'allsubject.html', context)
def pvideos(request, slug):
vds = Videos.objects.filter(slug=slug).first()
coursecontent = Videos.objects.filter(subject=vds)
context = {'vds':vds, 'coursecontent':coursecontent}
return render(request, 'pvideos.html', context)
models.py
class Videos(models.Model):
sno = models.AutoField(primary_key=True)
title = models.CharField(max_length=500)
cont = models.TextField()
vurl = models.URLField(max_length=200)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='videos')
position = models.PositiveSmallIntegerField(verbose_name="videono.")
slug = models.CharField(max_length=130)
timeStamp = models.DateTimeField(default=now)
def __str__(self):
return self.title
pvideo.html
{% extends 'base.html' %}
{% block title %}Free Video Course {% endblock title %}
{% block body %}
<div class="container">
<div class="embed-responsive embed-responsive-21by9">
<iframe class="embed-responsive-item" src="{{vds.vurl}}" allowfullscreen></iframe>
</div>
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active font-weight-bold" id="home-tab" data-toggle="tab" href="#overview" role="tab"
aria-controls="home" aria-selected="true">Overview</a>
</li>
<li class="nav-item">
<a class="nav-link font-weight-bold" id="profile-tab" data-toggle="tab" href="#coursecontent" role="tab"
aria-controls="profile" aria-selected="false">Course Content</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="overview" role="tabpanel" aria-labelledby="home-tab">
<h2>{{vds.title}}</h2>
<p>{{vds.cont|safe}}</p>
</div>
<div class="tab-pane fade" id="coursecontent" role="tabpanel" aria-labelledby="profile-tab">
{% for c in coursecontent %}
{{c.title}}
{% endfor %}
</div>
</div>
</div>
{% endblock body %}
I think I made a mistake on views.py coursecontent = Videos.objects.filter(subject=vds). I want to show this playlist on that page where I show lessions. I hope you understand what I want. If anyone has any other idea besides this, so please help me.
I user click on course content then I show all lession related to subject
You are right, you have made a mistake in coursecontent = Videos.objects.filter(subject=vds)
Subject must either be the pk or an instance of subject but you're giving it another video instance. What you want is
coursecontent = Videos.objects.filter(subject=vds.subject).exclude(sno=vds.sno)
.filter(subject=vds.subject) will give you all videos with the same subject.
.exclude(sno=vds.sno) will exclude the current video from the query. This is useful if you want to show only the other videos... If you want to include all, just skip that part.
As a side note, you might want to improve your variable naming.
vds could be called video (it's singular)
vurl could be just url
cont could be content
Most of your model names should be singular (coz each is a single instance).
An e-learning website is a nice initiative.
I want to create a dynamic input form and read the user inputs back when a button ("Submit") is pressed, what is the best way (a good way) to do this?
I have tried creating a dynamic form with a FieldList but I was unable to get the values from the user.
This is (hopefully all of) the relevant code:
forms.py
class EntryForm(Form):
field = StringField()
class DynamicForm(FlaskForm):
parameter = FieldList(FormField(EntryForm), min_entries=1)
submit = SubmitField('Submit')
routes.py
#app.route("/new", method=['GET', 'POST'])
def new_form():
form = DynamicForm()
if form.validate_on_submit():
values = form.parameter #This does not work as intended.
do_stuff(values)
return redirect(url_for('index'))
parameter = utils.get_parameter()
return render_template('dynamic_form.html', form=form, parameter=parameter)
where utils.get_parameter() returns an unknown list of parameters. Or to be precise the function expects a parameter and returns a list based on this paramater, but I have omitted this here.
dynamic_form.html
{% extends "layout.html" %}
{% macro render_field(item) %}
<div class="form-group">
<label class="form-control-label">{{ item.label }}</label>
<input type="text" name="{{ item.name }}" class="form-control" value="{{ item.value }}"/>
</div>
{% endmacro %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class ="form-group">
<legend class="border-bottom mb-4">Parameter</legend>
{% for item in form.parameter %}
{{ render_field(item) }}}
{% endfor %}
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
I assume it is because the validate_on_submit() is called after the form is constructed (again) and thus it does not contain the form fields, and especially not the user inputs. Is there a way to prevent this "data loss" or is this a completely wrong approach?
Also, please let me know if information is missing.
You don't want to validate the form when the page first loads. Try this first:
if request.method == "POST" and form.validate_on_submit():
values = form.parameter
do_stuff(values)
return redirect(url_for('index'))
Next, notice that you are redirecting them to your view for def index():. Is that what you want?