I am trying to create a simple Django 2.2 application with single model, single model form + custom field and a simple CreateView. I am populating the choices dynamically based on a http call to a outside url. The dropdown is populated fine, but when I try to submit my form I am getting an error:
Select a valid choice. ... is not one of the available choices and the form is refreshed with new 3 suggestions in the dropdown.
models.py
class GhostUser(models.Model):
first_name = models.CharField("User's first name", max_length=100, blank=False)
last_name = models.CharField("User's last name", max_length=100, blank=False)
ghost_name = models.CharField("User's ghost name", max_length=100, blank=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.ghost_name}"
def get_absolute_url(self):
return reverse('ghost_names:ghost-update', kwargs={'id': self.id})
views.py
class GhostCreateView(CreateView):
template_name = 'ghost_create.html'
form_class = GhostUserForm
success_url = '/'
# def get_context_data(self, **kwargs):
# data = super().get_context_data(**kwargs)
# url = "https://donjon.bin.sh/name/rpc-name.fcgi?type=Halfling+Male&n=3"
# resp = urllib.request.urlopen(url)
# names = resp.read().decode('utf-8')
# data['ghost_suggestions'] = names.splitlines()
# return data
forms.py
class GhostUserForm(forms.ModelForm):
ghost_name = forms.ChoiceField(choices=[], widget=forms.Select())
class Meta:
model = GhostUser
fields = ['first_name', 'last_name']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['ghost_name'].choices = tuple(get_ghost_names())
def get_ghost_names():
url = "https://donjon.bin.sh/name/rpc-name.fcgi?type=Halfling+Male&n=10"
resp = urllib.request.urlopen(url)
data = resp.read().decode('utf-8').splitlines()
names = []
for name in data:
existing_ghosts = GhostUser.objects.filter(ghost_name=name)
if existing_ghosts:
continue
else:
print(name.split())
if len(name.split()) > 1:
name = name.split()[0]
names.append((name, name))
return names[:3]
html
{% block content %}
<form action="." method="POST">{% csrf_token %}
{% for field in form.visible_fields %}
<p>
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
</p>
{% endfor %}
<input type="submit" value="Create ghost name">
</form>
{% comment %}{{ghost_suggestions}}
<select name="prefer_car_model" id="id_prefer_car_model" required>
<option value="0" selected disabled> Select ghost name </option>
{% for obj in ghost_suggestions %}
<option value="{{ obj }}">{{ obj }} </option>
{% endfor %}
</select>
{% endcomment %}
{% endblock content %}
What am I doing wrong here? I would appreciate your help on this weird for me issue.
P.S. When I add the commented out code from the view and template and render the fold fields one by one, the form submits without errors.
you did not mention the field name ghost_name in the form.py GhostUserForm class
change the line
fields = ['first_name', 'last_name'] to fields = ['first_name', 'last_name','ghost_name ']
Related
in my project, I have three models
models.py
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True, null=True, blank=True)
parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='children')
class Tag(models.Model):
tag = models.CharField(max_length=75, verbose_name='Tag')
slug = models.SlugField(null=True)
class Post(models.Model):
title = models.CharField(max_length=150)
slug = models.SlugField(max_length=150, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
tags = models.ManyToManyField(Tag, related_name='tags', blank=True)
and I created search filter view in views.py
def is_valid_queryparam(param):
return param != '' and param is not None
class SearchPepsiView(ListView):
template_name = "blog/NewSearch.html"
model = Post
paginate_by = 10
def get_queryset(self):
return Post.objects.filter(category__slug=self.kwargs['slug'])
def get_context_data(self, *args, **kwargs):
context = super(SearchPepsiView, self).get_context_data(*args, **kwargs)
context['category'] = Post.objects.filter(category__slug=self.kwargs['category'])
return context
def get(self, request, *args, **kwargs):
request = self.request
qs = Post.objects.all()
categories = Category.objects.filter(parent=None).order_by('name')
PostOrAuthor_query = request.GET.get('PostOrAuthor')
SearchCategory_query = request.GET.get('SearchCategory')
if is_valid_queryparam(PostOrAuthor_query):
qs = qs.filter(Q(title__icontains=PostOrAuthor_query) |
Q(content__icontains=PostOrAuthor_query) |
Q(author__username__icontains=PostOrAuthor_query)).distinct()
if is_valid_queryparam(SearchCategory_query) and SearchCategory_query != 'Choose...':
qs = qs.filter(category__name=SearchCategory_query)
count = qs.count() or 0
return render(request, self.template_name, {
'queryset': qs,
'categories': categories,
'count': count,
})
and I created Post Per Category View in views.py
class PostPerCategoryCBV(ListView):
model = Post
template_name = 'blog/Category_Projects.html'
def get_queryset(self):
self.category = Category.objects.get(slug=self.kwargs['slug'])
return Post.objects.filter(category=self.category)
def get_context_data(self, **kwargs):
context = super(PostPerCategoryCBV, self).get_context_data(**kwargs)
context['category'] = self.category
return context
and I created Post Per Tag View in views.py
class PostPerTagCBV(ListView):
model = Post
template_name = 'blog/tag_projects.html'
def get_queryset(self):
qs = super().get_queryset()
qs = qs.filter(tags__slug=self.kwargs['slug'])
return qs
and in templates, I created NewSearch.html
<form class="col-12 mt-5 mb-5">
<div class="form-group">
<label for="formGroupExampleInput">Post or Author</label>
<input type="search" name="PostOrAuthor" class="form-control" id="inputAddress" placeholder="Post or Author">
</div>
<div class="form-row">
<div class="form-group col-md-12">
<label for="SearchCategory">Category</label>
<select id="SearchCategory" name="SearchCategory" class="form-control">
<option selected>Choose...</option>
{% for cat in categories %}
<option value="{{ cat.name }}">{{ cat.name }}</option>
{% endfor %}
</select>
</div>
</div>
<button type="submit" class="btn btn-primary col-12">Search</button>
</form>
</div>
<div class="row">
<p>You have<b> {{ count }} </b>search results </p>
</div>
{% if category %}
<div class="row">
{% for post in category %}
{% include 'blog/Post_Loop.html' %}
{% endfor %}
</div>
{% else %}
<div class="row">
{% for post in queryset %}
{% include 'blog/Post_Loop.html' %}
{% endfor %}
</div>
{% endif %}
</div>
and Here is my URLs.py
path('post_list/', PostListCBV.as_view(), name='list-post'),
path("search-pepsi/", SearchPepsiView.as_view(), name="search-pepsi"),
path('tags_projects/<str:slug>/', PostPerTagCBV.as_view(), name='tag-posts'),
path('category_posts/<str:slug>/', SearchPepsiView.as_view(), name='category-posts'),
My success:
When all search fields are empty .. it list all posts.
when entering any search query .. it backs with the search result.
my problem is:
I believe that there are someway I can combine ListView of Post_Per_Category and Post_Per_Tag with Search View (in ONE View and one template) .. so I created def get_queryset(self): to use it but it didn't worked.
Anybody can help please ...
Thanks in-advance
I'm trying to get a list of objects in Django from a model.
I just want to get the list of 'dht node' from the request user, but it shows nothing in the html file (as if the list was empty). The user that I'm using has 2 'dht nodes' and they're shown in the django admin.
I don't know what is wrong, because if I use the instruction "member.dht.create(...)" in the views function and a create a new 'dht node' like this, this is shown. Only 'dht nodes' that I enter by form do not show. Can be the form?
Thanks a lot, Here's my code:
Models.py
class Node(models.Model):
name = models.CharField(primary_key=True, null=False, max_length= 50)
description= models.CharField(default=None, null=False, max_length= 250)
topic=models.CharField(default=None, null=False, max_length= 50, unique=True)
def __unicode__(self):
return self.name
class dht(Node):
temp = models.IntegerField(default=None, null=True)
hum = models.IntegerField(default=None, null=True)
class UserProfile(User):
uid = models.CharField(default=None, null=False, max_length= 250)
dht = models.ManyToManyField(dht, blank=True)
def __unicode__(self):
return self.user.username
Views.py -dht list-
#login_required(login_url = '/web/login')
def listDhtSensor(request):
member = request.user.userprofile
list = member.dht.all()
return render(request, 'web/listDhtSensor.html', {'list':list})
Html -listDhtSensor.html-
{% block content %}
{% for dht in list %}
{{ dht.name }}
{{ dht.topic }}
{% endfor %}
{% endblock %}
Forms.py
class newDHTSensorForm(forms.ModelForm):
class Meta:
model = dht
field = ['name',
'description',
'topic',]
labels = {'name': 'Name' ,
'description': 'Description',
'topic': 'Topic',}
exclude = ['temp', 'hum']
Views.py -dht form-
#login_required(login_url = '/web/login')
def newDHTSensor(request):
if request.method == "POST":
form = newDHTSensorForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.save()
return redirect('/web/dhtDetail')
else:
form = newDHTSensorForm()
return render(request, 'web/newDhtSensor.html', {'form': form})
Html -newDhtSensor.html-
{% block content %}
<div class="boxReg">
<form method="post">
{% csrf_token %}
<h2>{{ form.name.errors.as_text }}</h2>
<p><label for="id_name">Name:</label> <input class="barraForm" type="text" name="name" maxlength="150" autofocus="" required="" id="id_name"></p>
<p><label for="id_description">Description:</label> <input class="barraForm" type="text" name="description" maxlength="150" id="id_description"></p>
<h2>{{ form.topic.errors.as_text }}</h2>
<p><label for="id_topic">Topic:</label> <input class="barraForm" type="text" name="topic" maxlength="254" id="id_topic"></p>
<div class="boxButtonReg">
<button class="buttonReg" type="submit">Save</button>
</div>
</form>
</div>
{% endblock %}
It shows nothing because you did not link you dht objects to that UserProfile, so if you later query for the dhts for that UserProfile, evidently the list is empty. You should add it to the dht relation, like:
#login_required(login_url = '/web/login')
def newDHTSensor(request):
if request.method == "POST":
form = newDHTSensorForm(request.POST)
if form.is_valid():
post = form.save()
request.user.userprofile.dht.add(post)
return redirect('/web/dhtDetail')
else:
form = newDHTSensorForm()
return render(request, 'web/newDhtSensor.html', {'form': form})
Note that you first need to save the post, so you should omit the commit=False aprameter.
I'm trying to add functionality to add a IntegerField next to every 'stockItem' in the template so that the user can type how many of that item was needed and then update the 'count' value in the 'Stock' model. As for now it only works when the user only selects one item. (I know that how I implement this now would never work but I try to show how I intend it to work)
Models:
class Machine_Service(models.Model):
title = models.CharField(max_length = 100)
stockItem = models.ManyToManyField(Stock)
date = models.DateTimeField(default=timezone.now)
comment = models.TextField()
machine = models.ForeignKey(Machine, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Stock(models.Model):
title = models.CharField(max_length=100)
count = models.IntegerField(default=10)
minLimit = models.IntegerField(default=1)
resellerCompany = models.CharField(max_length=100)
resellerPerson = models.CharField(max_length=100)
resellerEmail = models.EmailField()
def __str__(self):
return self.title
(I left the 'Machine' model out of this because it does not belong to the question)
view:
def CreateService(request):
if request.method == 'POST':
form = CreateServiceForm(request.POST)
if form.is_valid():
m = Machine.objects.get(id=form['machine'].value())
service = Machine_Service(title=form['title'].value(), date=form['date'].value(), comment=form['comment'].value(), machine=m)
service.save()
items = form['stockItem'].value()
for item in items:
s = Stock.objects.get(id=item)
service.stockItem.add(s)
service.save()
return redirect('Machines-Home')
else:
form = CreateServiceForm()
context = {
'form': form
}
form:
class CreateServiceForm(forms.ModelForm):
count = forms.IntegerField(required=False)
class Meta:
model = Machine_Service
fields = ['title', 'stockItem', 'count', 'date', 'comment', 'machine']
template:
(I do not need to use crispy)
{% extends "maskinNytt/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Post!</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
In your case your models.py should look something like this:
class Machine_Service(models.Model):
stockItem = models.ManyToManyField(Stock, through='NeededItems')
...
class Stock(models.Model):
...
class NeededItem(models.Model):
machine_service = models.ForeignKey(Machine_Service, on_delete=models.CASCADE)
stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
amount = models.IntegerField()
To specify this amount and use it in your views/forms you need to just create instances of NeededItem.
I assume you have a form for the MachineService and one for a NeededItem. For the NeededItems you need a FormSet, views.py:
from django import formset_factory
NeededItemFormset = formset_factory(NeededItemForm, extra=2)
def view(request):
if request.method == 'POST':
machine_form = MachineForm(request.POST, prefix='machine')
needed_item_formset = NeededItemFormset(request.POST, prefix='items')
if machine_form.is_valid() and needed_item_formset.is_valid():
machine_form.save()
needed_item_formset.save()
return redirect('blabla')
else:
machine_form = MachineForm()
needed_item_formset = NeededItemFormset()
return render('template', {'m_form': machine_form, 'n_form': needed_item_formset})
The template then might look something like this.
<form>
{% machine_service_form.as_p %}
{% needed_item_formset.as_p %}
</form>
(I didn't test this, so I don't give guarantees it works, but I think you should have enough pointers to figure out which things you need).
When one would make a Machine_Service they would also need a form (or multiple) for the NeededItem model. That means you can basically ignore the Stock model when creating Machine_Services.
I just started learning Django and for this project I'm following the "Tango with Django" tutorial book. I have a problem with the input field of a form not showing up, while the button seems to be rendered fine.
Here's my code:
models.py
[...]
class Idea(models.Model):
keyword = models.ForeignKey(Keyword)
word = models.CharField(max_length=120)
count = models.IntegerField(default=1)
def __str__(self):
return self.word
forms.py
[...]
class Meta:
model = Keyword
fields = ('name',)
class IdeaForm(forms.ModelForm):
word = forms.CharField(max_length=120)
count = forms.IntegerField(widget=forms.HiddenInput(), initial=1)
class Meta:
model = Idea
fields = ('word',)
exclude = ('keyword',)
views.py
[...]
def keyword_detail(request, keyword_name_slug):
form = IdeaForm()
context_dict = {}
try:
keyword = Keyword.objects.get(slug=keyword_name_slug)
ideas = Idea.objects.filter(keyword=keyword)
context_dict['keyword'] = keyword
context_dict['ideas'] = ideas
except Keyword.DoesNotExist:
context_dict['keyword'] = None
context_dict['ideas'] = None
if request.method == 'POST':
form = IdeaForm(request.POST)
if form.is_valid():
idea = form.save(commit=False)
idea.keyword = keyword
idea.count = 1
idea.save()
return keyword_detail(request, keyword_name_slug)
else:
print(form.errors)
context_dict['form'] = form
return render(request, 'openminds/keyword.html', context_dict)
keyword.html
[...]
<h3>Add a new Idea</h3>
<div>
<form id="idea_form" method="post" action="">{% csrf_token %}
{% for hidden in forms.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in forms.visible_fields %}
{{ field.errors }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Add Idea" />
</form>
</div>
I think you're passing in form to the template, but attempting to use forms.
Im new in Django, and Im trying to create a form for add books for my app. But I want the date of publication not included in the form. Instead I want the current system date is obtained and will " add" the form to save my model . How could I do this?
There is part of my views.py:
def add_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
new_book = form.save(commit=False)
new_book.publication_date = django_timezone
new_book.save()
return HttpResponseRedirect('/thanks/')
else:
print form.errors
else:
form = BookForm()
return render_to_response('add_book.html',{'form':form})
There is my forms.py:
class BookForm(ModelForm):
class Meta:
model = Book
exclude = ('publication_date',)
And my model Book:
class Book(models.Model):
title = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank = True, null = True)
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
def __str__(self):
return self.title
I used this template for form:
{% extends 'base.html' %}
{% block title %}Add a new Book{% endblock %}
{% block content %}
<h3> Here you can add a new book into the local DataBase </h3>
<form action="." method="post">{% csrf_token %}>
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="id_title">Book Title</label>
{{ form.title }}
</div>
<div class="fieldWrapper">
{{ form.authors.errors }}
<label for="id_authors">Authores</label>
{{ form.authors }}
</div>
<div class="fieldWrapper">
{{ form.publisher.errors }}
<label for="id_publisher">Publishers</label>
{{ form.publisher }}
</div>
<div class="fieldWrapper">
{{ form.num_pages.errors }}
<label for="id_num_pages">Number of Pages</label>
{{ form.num_pages }}
</div>
<p><input type="submit" value="Submit"></p>
</form>
{% endblock %}
I've temporarily disabled Django csrf because I do not need for my purpose
To do that, you need to pass the default argument as timezone.now in publication_date model field.
models.py
class Book(models.Model):
title = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
# pass the default argument in publication_date field
publication_date = models.DateField(default=timezone.now)
num_pages = models.IntegerField(blank = True, null = True)
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
def __str__(self):
return self.title
views.py
After doing that, you can directly call .save() on modelform. Now, the book object will be created with the aware datetime.
def add_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
new_book = form.save() # directly save the book object
return HttpResponseRedirect('/thanks/')
else:
print form.errors
else:
form = BookForm()
return render_to_response('add_book.html',{'form':form})
Why we did not use auto_now_add argument in the model field?
From django's auto_now_add documentation:
Django's auto_now_add uses current time which is not timezone aware. So, it is better to explicitly specify default value using default argument.
If you want to be able to modify this field, set default=timezone.now
(from django.utils.timezone.now()) instead of auto_now_add=True.