Django Question 'ModelChoiceField' object has no attribute 'use_required_attribute' - python

Dears,
I met a problem in Django.
There is an error about 'ModelChoiceField' object has no attribute 'use_required_attribute' and I don't know where I made mistake.
My goal is to make my submit customizing form but I couldn't understand how CBV in Django can reach this goal, so I went back to use forms.py and FBV in Django. But my fields in form model have foreign keys to refer other models.
I used forms.ModelChoiceField and expected to solve foreign key problem but it doesn't work and show this error.
Thank you for reading and hope someone can help me to solve it.
Here are my code:
forms.py
from django import forms
from .models import Case
from school_app.models import School
from student_app.models import Student
class CaseModelForm(forms.ModelForm):
class Meta:
model = Case
fields='__all__'
widgets = {
'name' : forms.ModelChoiceField(queryset=Student.objects.all()),
'phone' : forms.TextInput(),
'school' :forms.ModelChoiceField(queryset=School.objects.all())
}
models.py
from django.db import models
from django.urls import reverse
import datetime,time
from school_app.models import School
from student_app.models import Student
# Create your models here.
class Case(models.Model):
name=models.ForeignKey(Student,related_name='case_student_name',on_delete=models.CASCADE,verbose_name="姓名")
phone=models.CharField(verbose_name="電話",blank=False,max_length=256)
school=school=models.ForeignKey(School,related_name='case_school',on_delete=models.CASCADE,verbose_name="學校")
views.py
def CaseFormView(request):
print("CaseForm")
form_cus = CaseModelForm()
if request.method == "POST":
form = CaseModelForm(request.POST)
if form.is_valid():
form.save()
return redirect("/")
context={
'form_cus': form_cus
}
return render(request, 'case_app/case_form.html',context)
urls.py
from student_app import views
from django.urls import path
app_name = 'student_app'
urlpatterns=[
path('', views.StudentListView.as_view(),name='list'),
path('<int:pk>/', views.StudentDetailView.as_view(), name='detail'),
path('create/',views.StudentCreateView.as_view(), name='create')]
case_form.html
<form method="post">
{% csrf_token %}
<div class="form-group">
<div class="form-group col-md-6">
<label>{{form_cus.name.label}}</label>
{{form_cus.name}}
</div>
<div class="form-group col-md-6">
<label>{{form_cus.school.label}}</label>
{{form_cus.school}}
</div>
{{form_cus}}
<input type="submit" class="btn btn-primary" value="提交">
</div>
{% if not form.instance.pk %}
<a class='btn btn-secondary' href="{% url 'case_app:list' %}">取消</a>
{% else %}
<a class='btn btn-secondary' href="{% url 'case_app:detail' pk=form.instance.pk %}">取消</a>
{% endif %}
</form>
Thank you!

A ModelChoiceField is not a widget, it is a form field, you thus can implement this as:
from django import forms
from .models import Case
from school_app.models import School
from student_app.models import Student
class CaseModelForm(forms.ModelForm):
school = forms.ModelChoiceField(queryset=School.objects.all())
name = forms.ModelChoiceField(queryset=Student.objects.all())
class Meta:
model = Case
fields='__all__'
widgets = {
'phone' : forms.TextInput(),
}
Since school and name are however ForeignKeys in your Case model, you do not need to specify these: we can implement the CaseModelForm with:
from django import forms
from .models import Case
from school_app.models import School
from student_app.models import Student
class CaseModelForm(forms.ModelForm):
# no school/name field
class Meta:
model = Case
fields='__all__'
widgets = {
'phone' : forms.TextInput(),
}

Related

Costumize HTML render of Django form with MultipleChoiceField and the widget CheckboxSelectMultiple

Im creating a Django form that allows to select multiple options (checkbox) and stores a list of characters in the database. The backend works as expected but the frontend renders not nicely.
Actually, there are no checkboxes appearing in html.
Here what I did in models.py
from django.db import models
class MyModel(models.Model):
field = models.CharField(max_length=70, default='good')
In forms.py
from django import forms
from .models import MyModel
CHOICES = (
('good', 'good'),
('c1', 'c1'),
('c2', 'c2'),
('c3', 'c3'),
('c4', 'c4'),
)
class MyForm(forms.ModelForm):
field = forms.MultipleChoiceField(
choices=CHOICES,
widget=forms.CheckboxSelectMultiple,
)
class Meta:
model = MyModel
fields = ['field', ]
In views.py
from django.contrib import messages
from django.shortcuts import render, redirect
from .forms import MyForm
def new_odontogram(request):
my_form = MyForm()
if request.method == 'POST':
my_form = MyForm(request.POST)
if my_form.is_valid():
my_form.save()
messages.success(request,'Data stored !')
return redirect('index')
return render(request, 'my_form_page.html', {'my_form': my_form})
The html code belows acts well for the backend, in my_form_page.html:
<form method="post">
{% csrf_token %}
{{ my_form.as_p }}
<a href="{% url 'index' %}">
<button type="submit">Record</button>
</a>
</form>
However, as I mentioned at the beginning, there are no boxes displayed and I wish to costumize them. So, I read somewhere that there there is some way to loop over the options and add a option that helps to display these boxes.
So I tried this:
{% for option in my_form.field %}
<label for="{{ option.id_for_label }}">
<input type="checkbox" value="{{ option.choice_label }}" id="{{ option.id_for_label }}">
<span class="checkable">{{ option.choice_label }}</span>
</label>
{% endfor %}
With that I can see the boxes displayed, but the backend is broken. I'd appreciate a lot your help to set up correctly this html loop, I guess the name argument is missing but I have no idea of what to include in.
This was run on Django 3.0.5 and Python 3.6.9, I'm using PicnicCSS for styling
Thanks in advance for your help

Django generic UpdateView returns 404 error: "No user found matching the query" when using pk

I am creating a Django web app and want to use the generic UpdateView by passing in a user's primary key. It works for the DetailView, but not the UpdateView.
I've tried specifying the template_name, changing the order of paths in my urls.py file. I haven't tried using a slug yet, but I know I should be able to use a pk.
I am using the built-in User model from django.contrib.auth below for my profile.
views.py:
class Profile(generic.TemplateView):
template_name = 'accounts/profile.html'
class ProfileUpdate(generic.UpdateView):
model = User
fields = ['first_name']
template_name = 'accounts/profile_update_form.html'
models.py
from django.db import models
from django.contrib import auth
# Create your models here.
class User(auth.models.User,auth.models.PermissionsMixin):
readonly_fields = ('id',)
def __str__(self):
return self.username
class UserProfile(models.Model):
user = models.OneToOneField(auth.models.User,on_delete=models.CASCADE)
join_date = models.DateTimeField(auto_now=True)
profile_pic = models.ImageField(upload_to='profile_pics',default='media/block-m.png')
skills = models.TextField()
major = models.CharField(max_length=128)
grad_year = models.CharField(max_length=4)
clubs = models.TextField() #make FK to Clubs
opt_in = models.BooleanField(default=True)
def __str__(self):
return self.user.username
My urls.py is what frustrates me the most. The address http://127.0.0.1:8000/accounts/profile/5/ works fine, but http://127.0.0.1:8000/accounts/profile/5/edit/ returns a 404 error "No user found matching the query". So I know that the pk=5 exists, but doesn't work for my url ending in /edit/.
urls.py:
from django.urls import path,include
from django.contrib.auth import views as auth_views
from . import views
app_name = 'accounts'
urlpatterns = [
path('login/',auth_views.LoginView.as_view(template_name='accounts/login.html'),name='login'),
path('logout',auth_views.LogoutView.as_view(),name='logout'),
path('signup/',views.SignUp,name='signup'),
path('profile/<int:pk>/',views.Profile.as_view(),name='profile'),
path('profile/<int:pk>/edit/',views.ProfileUpdate.as_view(),name='profile_update'),
]
profile_update_form.html:
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<div class="container">
<h2>Sign Up</h2>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form layout='inline' %}
<input type="submit" class="btn btn-primary" value="Sign Up">
</form>
</div>
{% endblock %}
That's because user.pk is not the same as primary key for the same users userprofile, if you check their respectable tables in database you will see the difference, yes it is confusing at first simplest solution I did is using signals:
def profile_creation_signal(sender, instance, created, **kwargs) :
if created:
UserProfile.objects.create(pk=instance.id, user=instance)
post_save.connect(profile_creation_signal, sender=User)
It should work!
path('<username>/', ProfileView.as_view(), name="public_profile"),
class ProfileView(DetailView):
model = User
template_name = "pages/public_profile.html"
context_object_name = "profile"
slug_field = "username"
slug_url_kwarg = "username"
You can replace -username- with unique fields

Django: Auto suggest in text field

I'm trying to work out how to have suggestions in a text field using Django (1.11). I've been going through the documentation of autocomplete-light but so far I've not been able to figure out my use case. The documentation on that package is not easy to understand for a total noob :)
I want a text field that gives suggestions as you type, were the suggestions come from the database.
E.g. if it's a list of food items the user types in 'C' and it suggest Chicken and Crisps as they have been entered by someone earlier. I also want the user to be able to enter Cheese even though it hasn't been entered before.
The suggestion "algorithm" just has to check if what has been entered matches the first characters of already existing items.
Here is a boiled down version of the django project:
urls.py
from django.conf.urls import url
from django.contrib import admin
from testapp.views import TestView
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'$',TestView.as_view())
]
models.py
from django.db import models
class TestModel(models.Model):
name = models.CharField(max_length=120)
def __str__(self):
return self.name
forms.py
from django import forms
from .models import TestModel
class TestFormClass(forms.ModelForm):
class Meta:
model = TestModel
fields = ('__all__')
views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .forms import TestFormClass
class TestView(CreateView):
form_class = TestFormClass
template_name = 'template.html'
success_url = "/"
template.html
<html>
<header><title>This is title</title></header>
<body>
Enter something <br>
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
</body>
</html>
I'm hoping someone has a relatively simple solution to add the this code, at the moment I'm not worried about authentication or anything, just looking for a basic solution.
You can make use of django-autocomplete-light

Django, Can't get delete button to work

I have a section of my site where an admin can add a widget. However the delete button to delete any current widgets is not working. I have this code implemented else where on my site and it is working.
I copied the same code from the other section of my site where this is being used. The other section of my site which uses this code is a little different in that a post can only be deleted be the user who created it, and the widgets can be delete by any user with the "access_level" field is equal to "admin". However, I am the only admin so it should still work. The page that the widget stuff is displayed on is only accessible if your "access_level" is equal to admin so I don't need to validate whether or not they have the permission before deleting. Please help.
widget_list.html:
{% extends "base.html" %}
{% block content %}
<div class="container">
<div class="content">
<div class="widgets-list">
{% for widget in widget_list %}
<h3>{{ widget.name }}</h3>
<h3>{{ widget.widget_order }}</h3>
<div>
<p>{{ widget.body }}</p>
</div>
{% if user.is_authenticated %}
<a class="auth-user-options" href="{% url 'adminpanel:delete-widget' pk=widget.pk %}">Delete</a>
{% endif %}
{% endfor %}
</div>
</div>
</div>
{% endblock %}
Adminpanel app views.py:
from django.shortcuts import render
from adminpanel.forms import WidgetForm
from adminpanel.models import Widget
from django.utils import timezone
from django.contrib.auth import authenticate,login,logout
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse,reverse_lazy
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from braces.views import SelectRelatedMixin
from django.views.generic import (TemplateView,ListView,
DetailView,CreateView,
UpdateView,DeleteView)
# Create your views here.
class CreateWidgetView(LoginRequiredMixin,CreateView):
login_url = '/login/'
redirect_field_name = 'index.html'
form_class = WidgetForm
model = Widget
def form_valid(self,form):
self.object = form.save(commit=False)
self.object.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('adminpanel:widgets')
class SettingsListView(ListView):
model = Widget
ordering = ['widget_order']
class DeleteWidget(LoginRequiredMixin,SelectRelatedMixin,DeleteView):
model = Widget
select_related = ('Widget',)
success_url = reverse_lazy('adminpanel:widget')
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user_id=self.request.user.id)
def delete(self,*args,**kwargs):
return super().delete(*args,**kwargs)
Project url spy:
from django.conf.urls import url
from django.contrib import admin
from django.conf.urls import include
from accounts import views
from colorsets import views
from colors import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$',views.home,name='index'),
url(r'^accounts/',include('accounts.urls',namespace='accounts')),
url(r'^colorsets/',include('colorsets.urls',namespace='colorsets')),
url(r'^adminpanel/',include('adminpanel.urls',namespace='adminpanel')),
]
Adminpanel app urls.py:
from django.conf.urls import url
from adminpanel import views
app_name = 'adminpanel'
urlpatterns = [
url(r'^widgets/',views.SettingsListView.as_view(),name='widgets'),
url(r'^new/$',views.CreateWidgetView.as_view(),name='create-widget'),
url(r'^delete/(?P<pk>\d+)/$',views.DeleteWidget.as_view(),name='delete-widget'),
]
EDIT: Here is the error I'm getting I forgot to add it.
FieldError at /adminpanel/delete/10/
Cannot resolve keyword 'user_id' into field. Choices are: body, id, name, widget_order
and the traceback points to this:
/Users/garrettlove/Desktop/colors/adminpanel/views.py in get_queryset
return queryset.filter(user_id=self.request.user.id) ...
▶ Local vars
Adminpanel app models.py (widget model):
from django.db import models
from adminpanel.choices import *
# Create your models here.
class Widget(models.Model):
name = models.CharField(max_length=50)
widget_order = models.IntegerField(blank=False,unique=True)
display_name = models.IntegerField(choices=WIDGET_NAME_CHOICES,default=1)
body = models.TextField(max_length=500)
def __str__(self):
return self.name
As your error is saying:
Cannot resolve keyword 'user_id' into field.
And it points to this line:
return queryset.filter(user_id=self.request.user.id)
It's that your queryset model does not have a user_id field. In your DeleteWidget view you have specified model = Widget
and as you see in your Widget model, you have the following fields: body, id, name, widget_order. There is no user_id field.
You need to change your filtering logic. If you want to have a related User model, you should use ForeignKey relation and then you can filter by the User's id.
In your models.py:
from django.contrib.auth.models import User
class Widget(models.Model):
# ...
user = models.ForeignKey(User, on_delete=models.CASCADE)
And then in your DeleteWidget's get_queryset, you can do the following:
return queryset.filter(user__id=self.request.user.id)
# __________________________^
# Note that there is a double underscore here!
# This is because Django uses double underscore for accessing related models in queries.

django form error :"the choice is not one of available choices"

I have 2 apps.
input: has the form containing a drop down list: region. The drop down list values are coming from the database of result (uploaded by user). When filling the form, user needs to select the value from the drop down list.
result:have the database
Now I can show the drop down list from the database of "input" app in the input form. (shown in picture).
The problem I face now is after selecting the choice and submit it, there is an error shown as below:
The error shows like: select a valid choice.The choice is not one of available choices. Then I don't see why because I indeed selected the choice from drop down list.
Thanks in advance for your help to pin point the problem.
models.py
from django import forms
from django.forms import ModelForm
from django.db import models
from dupont.models import Result
from datetime import date
from django.forms import widgets
class Input(models.Model):
company=models.CharField(max_length=100)
region=models.CharField(max_length=100)
def __unicode__(self):
return self.company
forms.py
from django import forms
from django.forms import ModelForm
from .models import Input
from dupont.models import Result
from django.contrib.auth.models import User,Group
from django.forms import widgets
from functools import partial
from django.forms.utils import ErrorList
class InputForm(forms.ModelForm):
company=forms.CharField(widget=forms.TextInput, label="Company",error_messages={'required': 'Please enter the company name'},required=True)
region = forms.ModelChoiceField(queryset=Dupont.objects.values('region').distinct(),widget=forms.Select(),empty_label="(Global)",to_field_name="supply_chain")
error_css_class='error'
required_css_class = 'required'
class Meta:
model = Input
fields = ('company', 'region')
views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render,render_to_response,get_object_or_404
from inputform.forms import InputForm
from inputform.models import Input
from dupont.models import Result
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.list import ListView
from django.contrib import messages
from django.template import RequestContext
from django.shortcuts import redirect
#csrf_exempt
def input(request):
if request.method == 'POST':
form = InputForm(request.POST)
if form.is_valid():
company = form.cleaned_data['company']
region = form.cleaned_data['region'] /Is this one correct?
form.save()
return redirect('result')
else:
print form.errors
else:
form=InputForm()
return render_to_response('inputform.html',{'form': form},context_instance=RequestContext(request))
html
<form method="post" action="{% url 'input' %}">
{% csrf_token %}
<!--company-->
<div class="field">
{{ form.company.errors }}
<label for="{{ form.company.id_for_label }}">Company:</label>
{{ form.company }}
</div>
<!--Region-->
<div class="field" >
<label> Select the Region:
{{ form.region }}
{% for region in form.region.choices %}
<option value="region" name= "region" id="id_region">{{region}} </option>
{% endfor %}
</label>
</div>
<!--submit-->
<div class="fieldWrapper">
<p><input type="submit" value="Submit" /></p></div>
</form>
Thanks for the post: django forms give: Select a valid choice. That choice is not one of the available choices
I changed in
iquery = Result.objects.values_list('region', flat=True).distinct()
iquery_choices = [('', 'None')] + [(region,region) for region in iquery]
region = forms.ChoiceField(iquery_choices,required=False, widget=forms.Select())
replacing the previous
region=forms.ModelChoiceField(queryset=Dupont.objects.values('region').distinct(),widget=forms.Select())"
and then it works. But I don't understand why, hope someone could explain.

Categories

Resources