How to display a dropdown list in Django form? - python

I have an issue with the display of a dropdown list, which is a field part of a Django form. Instead of a normal dropdown list, it appears like a kind of multiple select choice box (at least, it's high as this kind of object, as you will see in below screenshots), with the feature of a dropdown (the small arrow that opens the choice list).
I do not understand why it looks like this and how to solve this.
Edit
I pushed my current version into production, for tests and demo purposes, and surprisingly, it works, the dropdown displays properly (and still ugly in local environment)
If anyone has an explanation/solution for that, he is welcome
As far as possible I use standard objects (I'm not very comfortable with CSS) and, in this case, I did not manage to update anything (event setting height had no impact, maybe there is something wrong in this part too)
And I can understand my question is not perfect, but please explain me what's wrong to allow me to add missing information (and I still do not understand why we cannot thanks in advance people who will read and try to solve problems here, but it's another question... that will probably be edited without any explanation or comment)
Related model field is defined like this:
class Company(models.Model):
"""
Company informations
- Detailed information for display purposes in the application
but also used in documents built and sent by the application
- Mail information to be able to send emails
"""
company_name = models.CharField("nom", max_length=200)
comp_slug = models.SlugField("slug")
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")] # Default management rule
rule = models.CharField(
"mode de scrutin", max_length=5, choices=rules, default="MAJ"
)
The form has no dedicated rules, even if tried to add some (kept as comment in the code below):
class CompanyForm(forms.ModelForm):
company_name = forms.CharField(label="Société", disabled=True)
# rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
# rule = forms.ChoiceField(label="Mode de scrutin", choices=rules)
class Meta:
model = Company
exclude = []
Here is the view:
#user_passes_test(lambda u: u.is_superuser or u.usercomp.is_admin)
def adm_options(request, comp_slug):
'''
Manage Company options
'''
company = Company.get_company(comp_slug)
comp_form = CompanyForm(request.POST or None, instance=company)
if request.method == "POST":
if comp_form.is_valid():
comp_form.save()
return render(request, "polls/adm_options.html", locals())
And the part of HTML code:
<div class="row border mt-4">
<div class="col-sm-12">
<h5>Préférences de l'application</h5>
<div class="row">
<div class="col-sm-5 mt-2">
{{comp_form.use_groups}} <label for="{{comp_form.use_groups.label}}">{{comp_form.use_groups.label}}
</div>
<div class="col-sm-7 mt-2">
<p><label for="{{comp_form.rule.label}}">{{comp_form.rule.label}}</label> : {{comp_form.rule}}</p>
<p>{{comp_form.upd_rule}} <label for="{{comp_form.use_groups.label}}">{{comp_form.upd_rule.label}}</p>
</div>
</div>
</div>
</div>
The concern is the format of the field:
When the user click on the arrow, here is the display (there are only 2 options):
What did I wrong?
How can I change this (in views or HTML/CSS)?

In your Company model you have only used two choices:
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
When rendering this model form in HTML there should be only two options. If you want may options you should modify your model.

Related

How can I create a submit form in Django with a dropping down list?

I am just starting to work with Django and I have some problems with forms and dropping lists.
I have a model with two attributes, and I want to display one of the attributes in a dropping down list (this one will be unchangeable) and another one in a text field (this one will be changeable). Also, I have a submit button, so I want to change a second attribute in a text field and by pressing on the button. How can I do this? What would some examples be?
As you are starting to work with Django, you might or might not know about how Django handle forms.
In Django, forms can be handled in two ways:
User-created and managed forms (without any form class)
Class-managed forms (connected to Django models)
Documentation form Django Forms
Now let’s talk about the first type of forms (where you create your HTML form and manage the request sent to server):
These forms are simple to make and when there are only a few and are only suggested when you have a very small amount of inputs (I would say four or fewer inputs).
Here is a simple example of subscription of a newsletter with an email example.
<form id='sub-form' method="POST">
{% csrf_token %}
<div>
<input type="email" name="sub_email">
</div>
<input class="button" value="Subscribe" type="submit" id="subbutton">
</form>
So a very important thing to look at here is {% csrf_token %}, about which you can read more about here and about how it works and prevents cross-site request forgery. This token will be required when you make a request to Django server with any post request and data.
In this subscription form you see one <input> with name="sub_email". Take note of this as we will use this to get this value on the server as this is the key to its value, and then a simple Submit Button.
When you press Submit on a page let’s say url = "http://BASE_URL/home" you will receive a POST request on the view that handles that URL.
So now coming to the view.py, let’s say you only allow registered users to subscribe then the view will go something like this (assuming you are not expecting any other request from the home URL).
def home(request):
user=request.user
if request.method == "POST":
if user.is_authenticated:
email = request.POST['sub_email'] #Using name of input
#Logic to save this email
return HttpResponse("You are Subscribed",status=200)
else:
return HttpReposnse("You are not Authenticated",status=401)
else:
return render(request,"home.html")
Now as you are the expert of simple forms, let’s work with Django class-based forms.
These views are a little work when you have very few inputs, but they are a great help in manageability and when you have to work with large number of inputs.
You will request these Class Based Forms as in your question you are trying to send an instance of a model from your Models.py to a form to user.
I have a model of Posts that can be used for this example:
class Post(models.Model):
postTitle = models.CharField(max_length = 90,null=True)
subTitle = models.CharField(max_length = 160,null=True)
location = models.CharField(max_length = 3,default = 'IN',null=True)
Now according to your question, you are trying to let the user change one attribute, let’s say postTitle and for location you are not letting the user select one of the countries which is preselected and for your post.
Now we have to create a form for this. Forms in class based are created in Forms.py. If you don't have forms.py then you can create one right along models.py and views.py.
Now for the form, I would like to edit some existing data as you are saying one of the attributes (Fields) is fixed and another editable, but you get the value from the model.
class PostEditForm(ModelForm):
location = forms.CharField(label='Country ',widget=forms.Select(attrs={'class': 'Classes_HERE','placeholder':' Select a Country','disabled':'disabled'} ,choices=country_list),required=True)
class Meta:
model = Post
fields= ['postTitle','subTitle','location']
labels = {
'postTitle':'Title',
'subTitle':'Sub-Title',
}
widgets = {
'postTitle': forms.TextInput(attrs={'class': 'mention_class_here','placeholder':' Add Title'}),
'subTitle': forms.TextInput(attrs={'class': 'mention_class_here','placeholder':' Add Sub-Title'})
}
Attributes can be mentioned in forms fields the way I have mentioned them in the above example. I used disabled="disabled" to disable (not editable) location field and used forms.Select to make it drop down.
You might also see that I gave the location field a list to choose from. This is how you can create a list of your items. It's been quite some time when I wrote this, so there might be errors or it may not work for you, so just make sure you are referring to the current documentation and searching Stack Overflow for answers.
country_list = [
('', 'Select a Country'),
("AF", "Afghanistan"),
("AX", "Aland Islands"),
("AL", "Albania"),
("DZ", "Algeria"),
("AS", "American Samoa"),
("AD", "Andorra"),
("AO", "Angola"),
("AI", "Anguilla"),
("AQ", "Antarctica"),
("AG", "Antigua And Barbuda"),
("AR", "Argentina"),
("AM", "Armenia"),
("AW", "Aruba"),
.
.
.
Now this form can be passed as context in a view to an HTML page.
def editPost(request,post_id):
user=request.user
post = get_object_or_404(Post,id=post_id) #Getting the instance of Post
if user.is_authenticated:
formPost = PostEditForm(request.POST or None,instance=post)
if request.method=='POST':
if formPost.is_valid():
savedPost=formPost.save()
else:
return render(request,'postEdit.html',{'formPost':formPost})
else:
return HttpResponse("Not Authorized",status:401)
Now your HTML file postEdit.html should look something like this:
<form id="post-form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div>
{{formPost}}
</div>
</form>
That is it and adding a submit button in the same form, you can now edit your instance of post that you passed along with this {{formPost}}. Combine your logic wherever you think needs a change to fit in what you want to do.
By no means I am saying all this code is in working condition, but it is shown only to illustrate the flow and working.

Django ModelForm's DateTime value being localized/translated with localization turned off

So I'm working on this Activity management platform, we are using Django 3.0.4. Before I expose my problem, I must say that I'm somewhat new to Django and python itself.
My current objetive is to allow the administrator to set specific dates for specific events. Initially the page must show the current dates and details. That I can already the do with the code shown below
This is an example of the model used to store the dates
class Dates(models.Model):
id = models.AutoField(db_column='ID', primary_key=True)
datestart = models.DateTimeField(db_column='DateStart')
dateend = models.DateTimeField(db_column='DateEnd')
class Meta:
db_table = 'Dates'
This is my form
class dateForm(forms.ModelForm):
class Meta:
model = Dates
exclude = ['id']
And this is my current view function
def editdates(request):
dates_current = Dates.objects.get(id=1)
dates_form = dateForm(instance=dates_current)
if request.method == 'POST':
submitted_data = request.POST.copy() #Might need to change incoming data before saving to db
dates_form = diaAbertoSettingsForm(submitted_data, instance=dates_current) #Generate form with existing data
if dates_form.is_valid():
dates_form.save()
return render(request=request,
template_name = 'dates/edit.html',
context = {'form': dates_form})
The same page that shows the data (dates) must also allow you to submit a change, this would be relatively easy to do. The real problem starts when I want to submit the form.
Currently, I do the following to display the current values
<div class="column">
<div class="field">
{% if form.datestart.errors %}
<div class="alert alert-danger">
<strong>{{form.datestart.errors}}</strong>
</div>
{% endif %}
<label class="label">Date Start</label>
<div class="control">
<input class="input" type="text" name="datestart" value='{{form.datestart.value}}'>
</div>
</div>
</div>
Which is all fine, until you see that the result from {{form.datestart.value}} is not a normal "YY-MM-DD H:M:S" dateTime (e.g. 2000-01-01 00:00:00) format like you'd expect, but a "Written out" format like "Jan. 1, 2000, midnight" which obviously causes problems when I want the user to edit this date. If they were to keep the format, the form would return an error saying the format of the dateTime is invalid.
To fix this problem I've tried the following:
Turning off "Format Localization" in settings.py and on a template by template basis
Turning off "Translation", same as above
I've tried using the date filters described in the django documentation
I've also tried using the datetime parser from django utils
I've also looked around on other sources besides the documentation but none seem to share my problem.
If I were to place {{form.datestart}} instead of {{form.datestart.value}}, would indeed make it come out correct, but since I want to use a classes from bulma, I don't see a way around it.
How can I make it so the {{form.datestart.value}} comes out as 2000-01-01 00:00:00 and not as Jan. 1, 2000, midnight ?
This is only an example, the real model has about 10 diferent start/end dates, so a way to keep automated it, would be ideal but not required.
I fixed it. The solution was rather easy and in hindsight It's what I should've done in the beginning.
First of all, I turned off localization globally. Secondly I changed my modelForm to the following
class dateForm(forms.ModelForm):
class Meta:
model = Dates
exclude = ['id']
widgets = {
'datestart': DateTimeInput(attrs={'class': 'input'})
}
This allowed me to give custom classes to my fields. Then in the template I replaced <input class="input" type="text" name="datestart" value='{{form.datestart.value}}'> with the complete field {{form.datestart}}.

Add editable choices in django

I am trying to add a editable choicefield in django like the picture, already do some research on this. Unfortunately, the solution like django-autocomplete doesn't quite fulfill my needs. The autocomplete looks fine, but if do so, I need create a django modal to generate the choices through a url view request, but in my case, it doesn't necessary to do that, I just need put these 3 choices for ip address in the dropdown list, and choose one of them then edit it or submit.
The solutions I found, but they are not very well fit my need:
Django admin: Change selected box of related fields to autocomplete
Django editable dropdown field
If you want to use just those three entries the quickest way is to use a datalist, it just needs to be added into your HTML somewhere.
<div id="page-wrapper">
<label for="ip">Network address</label>
<input type="text" id="ip" list="ip-datalist">
<datalist id="ip-datalist">
<option>192.168.0.0/24</option>
<option>10.0.0.0/24</option>
<option>172.16.0.0/24</option>
</datalist>
</div>
If your options are likely to never change, you could hardcode them into your html like the answer above. If you're generating your dropdown using a form however it would be better to do something like this:
class MyForm(forms.Form):
ip_list = ((1, '192.168.0.0/24'), (2, '10.0.0.0/24'),
(3, '172.16.0.0/24'), )
network_address = forms.ChoiceField(label='Network Address',
choices=ip_list)
...
Once you render the form object in your template it ends up producing html like this:
<label for="id_network_address">Network Address:</label>
<select id="id_network_address" name="network_address">
<option value="1">192.168.0.0/24</option>
<option value="2">10.0.0.0/24</option>
<option value="3">172.16.0.0/24</option>
</select>
This way it is easier to change the ip_list in future if you need to, plus its keeps all your code in one place. This is explained in the docs here.

No Item matches the given query: dropdown options in Django

I asked this question earlier, but now I'm having trouble sorting out how to use drop downs (or even better, autofill fields) for one of the forms of a multi-form view.
The models in play are Book, BookDetails, and Genre. BookDetails is a linking table to Genre (and other similar tables) so that I can have a static list of genres etc. with unique IDs and foreign keys to BookDetails.
Right now I have this:
#views.py
def BookFormView(request):
genre = Genre.objects.all()
if request.method == "POST":
book_form = BookForm(request.POST, prefix='book')
bookdetails_form = BookDetailsForm(request.POST, prefix='bookdetails')
selected_genre = get_object_or_404(Genre, pk=request.POST.get('genre_id'))
genre.id = selected_genre
genre.save()
if book_form.is_valid() and bookdetails_form.is_valid():
book_form.save()
bookdetails_form.save()
return HttpResponseRedirect("/books/")
else:
book_form = bookForm(prefix='book')
bookdetails_form = BookDetailsForm(prefix='bookdetails)
return render(request, 'books/createbook.html',
{'book_form' : book_form,
'bookdetails_form': bookdetails_form,
'genre':genre,})
#createbook.html
<select name="genre", id="genre" form="bookform">
{% for entry in genre %}
<option value="{{ entry.id }}">
{{ entry.name }}
</option>
{% endfor %}
</select>
The form displays properly on the page, dropdown menu with options from the database included. However, when I hit submit to store the information to the database I get an error saying No Genre matches the given query The other posts on SO that regard this error don't seem to be from the same context. I think that it might be something to do with selecting a name but storing an id (for the genres), but otherwise I'm at a loss.
Normally, the way you'd do this with a form in django is not by manually pulling something out of the POST dict, but by using a ModelChoiceField:
https://docs.djangoproject.com/en/1.8/ref/forms/fields/#modelchoicefield
Was there a specific reason you didn't do that?
Also, it appears you're using the genre variable incorrectly for two different things. You initialize it with a queryset, but then try to treat it like a Genre instance later in the code. That's going to cause problems not to mention the fact that I don't think your genre.id = ... line is going to do what you expect it to.
Also, it's against style conventions to use title-casing for function names. If you're going to be doing much coding in Python, it's probably worth taking a look at the officially accepted PEP8 style guide here:
https://www.python.org/dev/peps/pep-0008/
There are a few other problems in the code but I'm not sure it's worth calling them out.

Google app engine ReferenceProperty relationships

I'm trying to get my models related using ReferenceProperty, but not have a huge amount of luck. I have 3 levels: Group, Topic, then Pros, and Cons. As in a Group houses many topics, and within each topic could be many Pros and Cons.
I am able to store new Groups nice and fine, but I don't have any idea how to store topics underneath these groups. I want to link from a page with a link "New topic" underneath each group, that takes them to a simple form (1 field for now). Obviously the URL will need to have some sort of reference to the id of the group or something.
Here are my models:
class Groups(db.Model):
group_user = db.UserProperty()
group_name = db.StringProperty(multiline=True)
group_date = db.DateTimeProperty(auto_now_add=True)
class Topics(db.Model):
topic_user = db.UserProperty()
topic_name = db.StringProperty(multiline=True)
topic_date = db.DateTimeProperty(auto_now_add=True)
topic_group = db.ReferenceProperty(Groups, collection_name='topics')
class Pro(db.Model):
pro_user = db.UserProperty()
pro_content = db.StringProperty(multiline=True)
pro_date = db.IntegerProperty(default=0)
pro_topic = db.ReferenceProperty(Topics, collection_name='pros')
class Con(db.Model):
con_user = db.UserProperty()
con_content = db.StringProperty(multiline=True)
con_date = db.IntegerProperty(default=0)
con_topic = db.ReferenceProperty(Topics, collection_name='cons')
And one function for the actual page I want to show the list of Groups, and then underneath their topics:
class Summary(webapp.RequestHandler):
def get(self):
groups_query = Groups.all()
groups = groups_query.fetch(1000)
template_values = {
'groups': groups,
}
path = os.path.join(os.path.dirname(__file__), 'summary.html')
self.response.out.write(template.render(path, template_values))
And finally the html:
<html>
<body>
New Group
<br>
{% for group in groups %}
<font size="24">{{ group.group_name|escape }}</font><br> by <b>{{ group.group_user }}</b> at <b>{{ group.group_date }}</b> {{ group.raw_id }}
<br>
<a href="/newtopic?id={{group.key.id}}" >New topice </a>
<br>
<blockquote>
{{ topics.topics_name }}
</blockquote>
{% endfor %}
</body>
</html>
Something that has side effects, such as altering the store (by creating a new object for example) should NOT be an HTTP GET -- GET should essentially only do "read" operations. This isn't pedantry, it's a key bit of HTTP semantics -- browsers, caches, proxies, etc, are allowed to act on GET as read-only operations (for example by caching results and not passing a request to the server if they can satisfy it from cache).
For modifications, use HTTP verbs such as POST (most popular essentially because all browsers implement it correctly) or for specialized operations PUT (to create new objects) or DELETE (to remove objects). I assume you'll be going to use POST to support a variety of browsers.
To get a POST from a browser, you need either Javascript wizardy or a plain old form with method=post -- I'll assume the latter for simplicity.
If you're using Django 1.0 (which app engine supports now), it has its own mechanisms to make, validate and accept forms based on models. Other frameworks have their own similarly advanced layers.
If you want to avoid "rich" frameworks you'll have to implement by hand templates for your HTML forms, direct them (via some kind of URL dispatching, e.g. in app.yaml) to a handler of yours implementing with a def post(self):, get the data from the request, validate it, form the new object, put it, display some acknowledgment page.
What part or parts of the procedure are unclear to you? Your question's title focuses specifically on reference properties but I'm not sure what problem they are giving you in particular -- from the text of your question you appear to be on the right tack about them.
Edit: the OP has now clarified in a comment that his problem is how to make something like:
"<a href="/newtopic?id={{group.key.id}}" >New topic </a>"
work. There's more than one way to do that. If the newtopic URL is served by a static form, the handler for the post "action" of that form could get back to that id= via the Referer: header (a notorious but unfixable mis-spelling), but that's a bit clunky and fragile. Better is to have the newtopic URI served by a handler whose def get gets the id= from the request and inserts it in the resulting form template -- for example, in a hidden input field. Have that form's template contain (among the other fields):
<INPUT TYPE=hidden NAME=thegroupid VALUE={{ theid }}> </INPUT>
put theid in the context with which you render that template, and it will be in the request that the def post of the action receiving the form finally gets.
Just to answer the question for others as you probably figured this out:
class NewTopic(webapp.RequestHandler):
def get(self):
groupId = self.request.get('group')
# either get the actual group object from the DB and initialize topic with topic_group=object as in 'Nick Johnson's answer, or do as follows
topic = Topic()
topic.name = self.request.get("topicname")
topic.reference = groupId
topic.put()
Thankyou for the reply.
Yeah I am aware of the get vs post. The class I posted was just to actually print all the Groups().
The issue I have is I'm unsure how I use the models to keep data in a sort of hierarchical fashion, with Groups > Topics > Pros/Cons.
Grabbing data is simple enough and I am using:
class NewGroupSubmit(webapp.RequestHandler):
def post(self):
group = Groups()
if users.get_current_user():
group.group_user = users.get_current_user()
group.group_name = self.request.get('groupname')
group.put()
self.redirect('/summary')
I need another function to add a new topic, that stores it within that group. So lets say a group is "Cars" for instance; the topics might be "Ferrari", "Porsche", "BMW", and then pros/cons for each topic. I realise I'm being a little vague, but it's because I'm very new to relational databasing and not quite used to the terminology.
I'm not quite sure what problem you're having. Everything you list looks fine - the ReferenceProperties are set up according to what one would expect from your dscription. The only problem I can see is that in your template, you're referring to a variable "topics", which isn't defined anywhere, and you're not iterating through the topics for a group anywhere. You can do that like this:
<html>
<body>
New Group
<br>
{% for group in groups %}
<font size="24">{{ group.group_name|escape }}</font><br> by <b>{{ group.group_user }}</b> at <b>{{ group.group_date }}</b> {{ group.raw_id }}
<br>
<a href="/newtopic?id={{group.key.id}}" >New topice </a>
<br>
Topics:
<ul>
{% for topic in group.topics %}
<li>{{topic.topic_name}}</li>
{% endfor %}
</ul>
{% endfor %}
</body>
</html>
To create a new topic, just use the constructor, passing in the required arguments:
mytopic = Topic(topic_name="foo", topic_group=somegroup)
Here, somegroup should be either a Group object, or a key for a Group object.

Categories

Resources