I am using appengine webapp2 as wsgihandler, jinja2 as the template engine and wtforms as the form module for its support to app engine models.
Following is my simple model:
class TaskCategory(db.Model):
title = db.StringProperty()
description = db.TextProperty()
class TaskList(db.Model):
title = db.StringProperty()
description = db.TextProperty()
category = db.ReferenceProperty(TaskCategory)
start_date = db.DateProperty()
target_finish_date = db.DateProperty()
Inside my handlers i write stuff as follows:
from wtforms.ext.appengine.db import model_form
model_dict = {'category': TaskCategory,
'task': TaskList}
class CreateForm(webapp2.RequestHandler):
def get(self, slug):
form = model_form(model_dict[slug]) # slug can either be category or task.
self.render_template('index.html', {'form': form()})
Following is my template:
<form method="POST" action"">
<table>
{% for field in form %}
<tr>{{ field.label }}</tr>
<tr>{{ field()|safe }}</tr>
<tr>
{% if field.errors %}
<td>
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</td>
{% endif %}
{% endfor %}
</table>
<input type="submit" class="btn" value="Submit Form"/>
</form>
Everything renders perfectly in the template, except the dropdown for the foreign key the values listed over there are something like:
<tr><label for="category">Category</label></tr>
<tr><select id="category" name="category"><option selected value="__None"></option><option value="ahhkZXZ-bmV3LXByb2plY3QtdGVtcGxhdGVyEgsSDFRhc2tDYXRlZ29yeRgCDA"><models.TaskCategory object at 0xb22d74c></option>
<option value="ahhkZXZ-bmV3LXByb2plY3QtdGVtcGxhdGVyEgsSDFRhc2tDYXRlZ29yeRgDDA"><models.TaskCategory object at 0xb22dbec></option>
<option value="ahhkZXZ-bmV3LXByb2plY3QtdGVtcGxhdGVyFgsSDFRhc2tDYXRlZ29yeSIEdGVzdAw"><models.TaskCategory object at 0xb22d74c></option></select></tr>
As is visible the names are not being displayed for the category, instead the objects are displayed, how can i rectify it, in a generic manner?
Well this has got nothing to do with, WTForm or jinja or webapp2.
Change your database file to return the title in your case, instead of the object using repr as follows:
class TaskCategory(db.Model):
title = db.StringProperty()
description = db.TextProperty()
def __repr__(self):
return unicode(self.title)
Related
When I am filtering for my search bar I am getting this error. I am not sure what I am doing wrong here
Watching this tutorial: https://www.youtube.com/watch?v=llbtoQTt4qw&t=3399s
views.py
class pplList(LoginRequiredMixin,ListView):
model = People
context_object_name = 'people'
def get_context_data(self, **kwargs):
search_input = self.get.GET.get('search-area') or ''
if search_input:
context['people'] = context['people'].filter(name__icontains=search_input)
return context
people_list.html
{%if request.user.is_authenticated %}
<p>{{request.user}}</p>
Logout
{% else %}
Login
{% endif %}
<hr>
<h1>Interviewee Dashboard {{color}}</h1>
Add Candidates
<form method="get">
<input type = 'text' name = 'search-are'>
<input type = 'submit' value = 'Search'>
</form>
<table>
<tr>
<th> Item</th>
<th> </th>
</tr>
{% for people in people %}
<tr>
<td>{{people.name}}</td>
<td>View</td>
<td>Edit</td>
<td>Delete</td>
</tr>
{% empty %}
<h3>No items in list</h3>
{% endfor %}
</table>
There are some minor mistakes such as it should be self.request.GET.get('search-area'), also you haven't called the super() method, so try this view:
class pplList(LoginRequiredMixin,ListView):
model = People
context_object_name = 'people_list'
def get_context_data(self, **kwargs):
context=super().get_context_data(**kwargs)
search_input = self.request.GET.get('search-area', False)
if search_input:
context['people']= People.objects.filter(name__icontains=search_input)
return context
In the Html, the field name search-are and you are accessing in the view as self.request.GET.get('serach-area', False) kindly change its name to search-area` in the input tag of Html.
Also, class based views are generally written in PascalCase as they are classes of Python and requires their name to be written as model name as prefix and actual view name as suffix, so it should be PeopleListView.
So, I try to display a list of items from database, but after calling return render(...) it behaves as if there were no objects in the database. I am new to django and after a second day of trial and error I have no idea what to do with it
Affected view:
class DropDownList(ListView):
context_object_name = 'drop_down'
template_name="browse.html"
model=ServiceList
asd = ''
def post(self, request):
name = request.POST.get('selected')
obj = ServiceList.objects.get(sourceFile=name)
print(obj)
print(request.POST) #some prints for debugging
context = {'asd': obj}
return render(request, self.template_name, context)
Models:
class ServiceList(models.Model):
version = models.IntegerField()
location = models.CharField(max_length=500)
services = models.ManyToManyField(Service)
drms = models.ManyToManyField(Drm)
sourceFile = models.CharField(max_length=500)
class Service(models.Model):
uid = models.CharField(max_length=255, unique=True)
locations = models.ManyToManyField(Location)
names = models.ManyToManyField(Name)
category = models.CharField(max_length=500)
drm = models.CharField(max_length=500)
description = models.CharField(max_length=500)
html fragment:
<p>
<br/>
<form action="/browse/" id="tableform" method="POST">{% csrf_token %}
<select class="form-select" aria-label="Default select example" form="tableform" name="selected">
<option selected>-----------------</option>
<option >One</option>
<option >Two</option>
<option >Three</option>
{% for obj in drop_down %}
<option name={{obj.sourceFile}}>{{obj.sourceFile}}</option>
{% endfor %}
</select>
</form>
<button id="add-list" type="submit" class="btn btn-success pull-left" class="button1" form="tableform">+</button>
</p>
<table class="table table-bordered table-dark">
<tr>
<th>UID</th>
<th>Category</th>
</tr>
{% for obj in drop_down %}
adasdasd
{% if obj.sourceFile == asd %}
<br/>fghfghfgh
{% for service in obj.services.all %}
<tr>
<td>{{service.uid}}</td>
<td>{{service.category}}</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
</table>
There are a few things wrong in your code, I'm going to list them, so it is easier to understand.
1 - context_object_name = 'drop_down' is not being used, 'drop_down' is not being declared in your class.
2 - You don't need to assign 'asd' as an empty variable, since you are only using as a namespace for the obj variable in your context set.
3 - Your return function is wrong, you should only be returning the context there, since you are using a class based view.
4 - You are using a ListView to get only one object
I don't know if this is going to work(I don't have a lot of experience in class based views) but try to make your code look like this:
class DropDownList(DetailView):
template_name="browse.html"
model=ServiceList
def post(self):
name = request.POST.get('selected')
obj = ServiceList.objects.get(sourceFile=name)
context = {'asd': obj}
return context
you can delete the context_object_name line as I mentioned, so if you want to render your ServiceList object version field in and HTML page for example, you should use {{ asd.version }}
Also, try to look for function based views, they are better to use, im assuming that you are using this view to get an object from your database, if you were using a function based view here, your code would be much simpler, it would look like this
def get_object(request, object_id):
template = 'browse.html'
obj = ServiceList.objects.get(pk=object_id)
context = { 'youcandecideanamehere': obj }
return render(request, template, context)
but you would need to pass the object_id parameter in your url(I'm going to create some here, since you didn't give yours)
urlpatterns = [
...
path('get_object/<object_id>/', get_objects, name='get_objects')
...
]
And the a tag for that page, should be like this:
Text
Hope I've helped, but if you still have some problems, feel free to contact me.
I want my dropdown to get populated with values from one of my db tables in my Django project, yet it remains blank and I can't figure out why.
This is my html code of my home.html page:
<select name="regions" id="regions">
{% for item in regions_list %}
<option val="{{ item.name_reg }}"> {{ item.name_reg }} </option>
{% endfor %}
</select>
models.py:
class data_reg(models.Model):
id = models.AutoField(primary_key=True)
name_reg = models.TextField(db_column='NAME_REG', blank=True, null=True)
class Meta:
managed = True
db_table = 'reg'
views.py:
def MyView(request):
regions_list = RegionChoiceField()
query_results_dict = {
'regions_list': regions_list,
}
return render(request,'home.html', query_results_dict)
forms.py:
class RegionChoiceField(forms.Form):
regions = forms.ModelChoiceField(
queryset=data_immo.objects.values_list("name_reg", flat=True).distinct(),
empty_label=None
)
when passing ModelChoiceField to context I think the template should be different.
{% for item in regions_list %}
<option val="{{ item.name_reg }}"> {{ item.name_reg }} </option>
{% endfor %}
change to
{% for item in regions_list %}
{{ item }}
{% endfor %}
or even simpler, just put your form in the template
{{regions_list}}
Hope this works,
Greeting
Ken
I just tested this and it seems to work for me.
Can you print the query in your forms.py
print(data_immo.objects.values_list("name_reg", flat=True).distinct())
This will show the query set in your terminal:
<QuerySet ['a_value']>
I find it always good to print at various levels for debugging, fast and easy.
I managed to make it work and did it by avoiding using forms.py. I simply generated my 'regions_list' variable directly in views.py and only after that it managed to get properly recognized. So this is how it finally looked like:
def MyView(request):
regions_list = data_immo.objects.values_list("name_reg", flat=True).distinct()
query_results_dict = {
'regions_list': regions_list,
}
return render(request,'home.html', query_results_dict)
Also, I amended my html code slightly, as per Ken's suggestion:
<select name="regions" id="regions">
{% for item in regions_list %}
<option val="{{ item.name_reg }}"> {{ item}} </option>
{% endfor %}
</select>
I have the following models defined:
class Item(models.Model):
rfid_tag = models.CharField()
asset = models.OneToOneField('Assets', default=None, null=True,
on_delete=models.SET_DEFAULT,)
date = models.DateTimeField(name='timestamp',
auto_now_add=True,)
...
class Assets(models.Model):
id = models.AutoField(db_column='Id', primary_key=True)
assettag = models.CharField(db_column='AssetTag', unique=True, max_length=10)
assettype = models.CharField(db_column='AssetType', max_length=150)
...
class Meta:
managed = False
db_table = 'Assets'
ordering = ['assettag']
def __str__(self):
return f"{self.assettag}"
def __unicode__(self):
return f"{self.assettag}"
For which I have created the following form and formset:
class ItemDeleteForm(forms.ModelForm):
asset = forms.CharField(required=True,
help_text= "Item asset tag",
max_length=16,
label="AssetTag",
disabled=True,
)
delete = forms.BooleanField(required=False,
label="Delete",
help_text='Check this box to delete the corresponding item',
)
class Meta:
model = Item
fields = ['asset']
ItemDeleteMultiple = forms.modelformset_factory(model=Item,
form=ItemDeleteForm,
extra=0,
)
managed by the view:
class DeleteMultipleView(generic.FormView):
template_name = '*some html file*'
form_class = ItemDeleteMultiple
success_url = reverse_lazy('*app_name:url_name*')
def form_valid(self, form):
return super().form_valid(form)
And rendered in the template:
{% extends "pages/base.html" %}
{% block title %}
<title>Delete Multiple</title>
{% endblock %}
{% block content %}
<h1>Delete Multiple Items</h1>
<br>
<form class="ManualForm" action ="." method="POST"> {% csrf_token %}
{{ form.management_form }}
<table border="2">
<tr><th colspan="3" scope="row">Select Items to Delete</th></tr>
{% for item_form in form %}
<tr>
<td><label for="{{ item_form.asset.id_for_label }}">AssetTag {{forloop.counter}}:</label>
{% if item_form.non_field_errors %}
{{ item_form.non_field_errors }}
{% endif %}
{% if item_form.asset.errors %}
{{item_form.asset.errors}}
{% endif %}
</td>
<td>{{item_form.asset}}</td>
<td>{{item_form.delete}}
{% if item_form.delete.errors %}
{{item_form.delete.errors}}
{% endif %}
</td>
</tr>
{% endfor %}
</table>
<br>
<input class = "btn btn-success" type="submit" value="Delete Selected" />
Cancel
</form>
<form class="AutoForm" action ="." method="POST"> {% csrf_token %}
{{form.as_table}}
<input class = "btn btn-success" type="submit" value="Delete Selected" />
Cancel
</form>
{% endblock %}
When I submit AutoForm, everything is great. It takes me to app_name:url_name, but if I sumbit ManualForm I don't get redirected. It will simply clear all data and reload the form page with empty fields.
The HTTP POST response status code for AutoForm is 302, while for ManualForm is 200.
I don't understand how the template could influence the behavior of the url redirection. What am I doing wrong in the manual rendering of the formset?
It seems that adding:
{% for field in item_form.hidden_fields %}
{{field}}
{% endfor %}
under {% for item_form in form %} will solve the issue.
I didn't understand very well from the docs:
Looping over hidden and visible fields
If you’re manually laying out a form in a template, as opposed to
relying on Django’s default form layout, you might want to treat
< input type="hidden"> fields differently from non-hidden fields. For
example, because hidden fields don’t display anything, putting error
messages “next to” the field could cause confusion for your users – so
errors for those fields should be handled differently.
I just thought this is about errors, so I didn't care. But one of the first thing it says about forms is this:
As an example, the login form for the Django admin contains several
< input> elements: one of type="text" for the username, one of
type="password" for the password, and one of type="submit" for the
“Log in” button. It also contains some hidden text fields that the
user doesn’t see, which Django uses to determine what to do next.
It also tells the browser that the form data should be sent to the URL
specified in the < form>’s action attribute - /admin/ - and that it
should be sent using the HTTP mechanism specified by the method
attribute - post.
Maybe it will help someone else.
I want to save stream field ID into it's template.
In short, in text_question.html I am giving id = {{ self.id }} but that return Nothing.
I want this because in question.html file I want it to compare with {{
field.id }} which return stream field ID
In another word, I want to store {{ field.id }}'s value in id field of text_question.html
models.py
class TextQuestionBlock(blocks.StructBlock):
"""Text Question"""
question = blocks.CharBlock(required=True, help_text="Add your Question")
is_save = blocks.BooleanBlock(label="Want to save this field ?", required=False)
is_email = blocks.BooleanBlock(label="Want to get this field as an email ?", required=False)
class Meta: # noqa
template = "question/question_field/text_question.html"
icon = "edit"
label = "Text Question"
#register_setting(icon='fa-commenting')
class QuestionSettings(BaseSetting):
body = StreamField([
("text_question", TextQuestionBlock()),
], verbose_name='Question', blank=True)
panels = [
StreamFieldPanel('body')
]
class Meta:
verbose_name_plural = 'Question'
verbose_name = 'Questions'
text_question.html
{% load tag_library %}
<input issave="{{self.is_save}}" isemail="{{ self.is_email }}" class="text_question" type="text" name="{{ self.question|to_name }}" id="{{ self.id }}" data-conv-question="{{ self.question }}"
question.html
<form name="question_form" action="" method="post" class="hidden">
<div id="unique_id"></div>
{% for field in question.body %}
{{ field.id }}
{% endfor %}
<input type="text" data-conv-question="test">
</form>
Thank You!!!
The ID is not a built-in property of the block value - rather, it's a mechanism used by the StreamField container to keep track of its contents. It's not always meaningful for a block value to have an ID property: for example, the value of a CharBlock is a string, and you can't really have an .id property on a string. Similarly, child blocks of StructBlock won't be given one.
As a result, the id is not automatically available on a block's template - if you want it, you need to pass it explicitly from the calling template, via the {% include_block %} template tag. For example:
{% for field in question.body %}
{% if field.block_type == 'text_question' %}
{% include_block field with block_id=field.id %}
{% endif %}
{% endfor %}
This will make the ID available on text_question.html as the variable block_id.