ModelChoiceField in django admin gives 'Select a valid choice. That choice is not one of the available choices.' error - django-views

Hej!
I want a field in my django admin area where the user can select from given choices in the database.
For example get a list of countries and choose one. But I will always get the ' Select a valid choice. That choice is not one of the available choices.' error when you try to save the new instance.
#models.py
class PlaceSection(models.Model):
code = models.CharField(max_length=1)
def code_str(self):
return self.code
# admin.py
class InstiForm(forms.ModelForm):
place_sections = forms.ModelChoiceField(
PlaceSection.objects.values(),
widget=Select2Widget,
)
class Meta:
model = Something
fields = [
"place_section"]
class InstiAdmin(admin.ModelAdmin):
form = InstiForm
save_on_top = True
def save_model(self, request, obj, form, change):
fieldsets = [
(
("General"),
{"fields": [
"place_sections"
]
}
)
]
I do get the right choices in the admin dropdown but when I select one and save the error occurs.
Does anyone has an idea how to fix this (in the admin) found only similar problems without the admin part and no solution worked for me.
Help is really appreciated! :)
Edit:
I fixed the issue in my admin-area but now can't filter for PlaceSection in my forms/views.
The needed fields are there, but after the 'search' the TypeError 'PlaceSection' object is not iterable occurs. It seems to be a NoneType and empty but I don't understand what I'm doing wrong.
Any further help is appreciated as well! :)
# forms.py
class SearchForm(forms.Form):
place_sections = forms.ModelMultipleChoiceField(
queryset=PlaceSection.objects.all(), widget=Select2MultipleWidget, required=False)
# views.py
def search_institution(request):
if request.method == "POST":
form = SearchForm(request.POST)
if form.is_valid():
query = filter_query(form)
context = {"result_data": serialize(query), "form": form, "show_form": False}
return render(request, "stakeholders/search_form.html", context)
else:
form = SearchForm()
context = {
"result_data": serialize(Institution.objects.all()),
"form": form,
"show_form": True
}
return render(request, "stakeholders/search_form.html", context)
# query.py
def filter_query(form):
query = Institution.objects.all()
if form.cleaned_data["place_sections"]:
place_sections_query = Institution.objects.none()
for section in form.cleaned_data["place_sections"]:
place_sections_query = (
place_sections_query
| Institution.objects.filter(place_sections__in=section)
)
query = query.intersection(place_sections_query)
return query

ModelChoiceField accepts a queryset argument. Your code is missing that.
place_sections = forms.ModelChoiceField(
PlaceSection.objects.values(),
widget=Select2Widget,
)
should be as follows:
place_sections = forms.ModelChoiceField(
queryset=PlaceSection.objects.all(),
widget=Select2Widget,
)
And probably not values() but .all()
Check out the documentation: https://docs.djangoproject.com/en/3.2/ref/forms/fields/#django.forms.ModelChoiceField

#moddayjob helped me a lot!
Complete solution for my problem is:
# models.py
class PlaceSection(models.Model):
code = models.CharField(max_length=1)
def code_str(self):
return self.code
def __str__(self):
return f"{self.code}, {self.description_german}"
class Institution(models.Model):
place_sections = models.ManyToManyField(
PlaceSection,
)
def serialize(self):
return {"place_sections": ", ".join([place.code for place in self.place_sections.all()])}
# admin.py
#admin.register(NaceSection)
class PlaceSectionAdmin(admin.ModelAdmin):
search_fields = [
"code",
"place_sections",
]
class InstitutionForm(forms.ModelForm):
model = Institution
fields = [
"place_sections"
]
#admin.register(Institution)
class InstitutionAdmin(admin.ModelAdmin):
form = InstitutionForm
save_on_top = True
fieldsets = ["place_sections"]
autocomplete_fields = ["place_sections"]
# query.py
def filter_query(form):
query = Institution.objects.all()
if form.cleaned_data["place_sections"]:
place_sections_query = (
Institution.objects.filter(place_sections__in=form.cleaned_data["place_sections"])
)
query = query.intersection(place_sections_query)
# forms.py
class SearchForm(forms.Form):
place_sections = forms.ModelMultipleChoiceField(
queryset=PlaceSection.objects.all(), widget=Select2MultipleWidget, required=False)

Related

AttributeError: 'QuerySet' object has no attribute 'objects'

trying to upload the image using the django rest framework,I just do not know how to code the logic,,,here is my
models.py
....
class ProductsTbl(models.Model):
....
model_number = models.CharField(
max_length=255,
unique=True,
error_messages={
'unique': "這 model number 已經被註冊了 ."
}
)
slug = models.SlugField(unique=True)
....
class Upload(models.Model):
thing = models.ForeignKey(ProductsTbl, related_name="uploads")
......
app/api/serializers.py
........
class UploadSerializers(serializers.ModelSerializer):
image = serializers.ImageField(max_length=None, use_url=True)
class Meta:
model = Upload
fields = (
'id',
'image',
'thing',
'description'
)
class ProductsTblSerializer(serializers.ModelSerializer):
uploads = UploadSerializers(many=True, read_only=True)
......
class Meta:
model = ProductsTbl
fields = (
.....
'model_number',
'slug',
........
'uploads'
)
.....
app/api/urls.py
....
urlpatterns = [
url(r'^productsTbls/$',
views.ProductsTblListView.as_view(),
name='productsTbls_list'),
url(r'^productsTbls/(?P<pk>\d+)/$',
views.ProductsTblDetailView.as_view(),
name='productsTbls_detail'),
url(r'^productsTbls/(?P<slug>[-\w]+)/uploads/$',
views.UploadDetailView.as_view(),
name='uploads'),
]
app/api/views.py
......
class ProductsTblListView(generics.ListCreateAPIView):
queryset = ProductsTbl.objects.order_by('-created')
serializer_class = ProductsTblSerializer
class ProductsTblDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = ProductsTbl.objects.all()
serializer_class = ProductsTblSerializer
class UploadDetailView(CreateAPIView):
queryset = ProductsTbl.objects.all()
slug = queryset.objects.slug()
thing = ProductsTbl.objects.get(slug=slug)
Upload.objects.create(
image=thing.cleaned_data['image'],
description=thing.cleaned_data['description'],
thing=thing,
)
queryset = thing.objects.save()
serializer_class =
UploadSerializers
......
I got the error in termial console
AttributeError: 'QuerySet' object has no attribute 'objects'
here is my app/views.py in form ,I can use it to upload image ok
app/views.py
#login_required
def edit_thing_uploads(request, slug):
# grab the object...
thing = ProductsTbl.objects.get(slug=slug)
# double checking just for security
# if thing.user != request.user:
# raise Http404
form_class = ProductsTblUploadForm
# if we're coming to this view from a submitted form,
if request.method == 'POST':
# grab the data from the submitted form, # note the new "files" part
form = form_class(
data=request.POST,
files=request.FILES,
instance=thing
)
if form.is_valid():
thing = form.save(commit=False)
thing.useredit = request.user.username
thing.save()
# create a new object from the submitted form
Upload.objects.create(
image=form.cleaned_data['image'],
description=form.cleaned_data['description'],
thing=thing,
)
return redirect('edit_thing_uploads', slug=thing.slug)
# otherwise just create the form
else:
form = form_class(instance=thing)
# grab all the object's images
uploads = thing.uploads.all()
# and render the template
return render(request, 'things/edit_thing_uploads.html', {
'thing': thing,
'form': form,
'uploads': uploads,
})
the urls.py for app/views.py
project/urls.py
url(
r'^things/(?P<slug>[-\w]+)/edit/images/$',
views.edit_thing_uploads,
name='edit_thing_uploads'
),
however, when comes to django rest framework I just do not konw how to code the logic??

How can I change the queryset of one of the fields in the form I'm passing to inlineformset_factory

I'm using django extra views:
# views.py
from django.forms.models import inlineformset_factory
from extra_views import (CreateWithInlinesView, UpdateWithInlinesView,
InlineFormSet, )
class LinkInline(InlineFormSet):
model = Link
form = LinkForm
extra = 1
def get_form(self):
return LinkForm({})
def get_formset(self):
return inlineformset_factory(self.model, self.get_inline_model(), form=LinkForm, **self.get_factory_kwargs())
class TargetCreateView(BaseSingleClient, CreateWithInlinesView):
model = Target
form_class = TargetForm
inlines = [LinkInline, ]
template_name = 'clients/target_form.html'
I want this 'keywords' field to change based on the pk I pass to the view through the url.
# forms.py
class LinkForm(forms.ModelForm):
keywords = forms.ModelMultipleChoiceField(queryset=ClientKeyword.objects.filter(client__pk=1))
class Meta:
model = Link
I could manage to overwrite the form's init, however:
I don't have access to self.kwargs inside LinkInline
Even if I did, I'm not sure I can pass an instantiated form to inlineformset_factory()
Ok, if any poor soul needs an answer to how to accomplish this... I managed to do it by overwriting construct_inlines() (which is part of extra_views.advanced.ModelFormWithInlinesMixin) and modifying the field's queryset there.
class TargetCreateView(BaseSingleClient, CreateWithInlinesView):
model = Target
form_class = TargetForm
inlines = [LinkInline, ]
template_name = 'clients/target_form.html'
def construct_inlines(self):
'''I need to overwrite this method in order to change
the queryset for the "keywords" field'''
inline_formsets = super(TargetCreateView, self).construct_inlines()
inline_formsets[0].forms[0].fields[
'keywords'].queryset = ClientKeyword.objects.filter(
client__pk=self.kwargs['pk'])
return inline_formsets
def forms_valid(self, form, inlines):
context_data = self.get_context_data()
# We need the client instance
client = context_data['client_obj']
# And the cleaned_data from the form
data = form.cleaned_data
self.object = self.model(
client=client,
budget=data['budget'],
month=data['month']
)
self.object.save()
for formset in inlines:
f_cd = formset.cleaned_data[0]
print self.object.pk
link = Link(client=client,
target=self.object,
link_type=f_cd['link_type'],
month=self.object.month,
status='PEN',
)
# save the object so we can add the M2M fields
link.save()
for kw in f_cd['keywords'].all():
link.keywords.add(kw)
return HttpResponseRedirect(self.get_success_url())

How to hide some fields in django-admin?

class Book(models.Model):
title = models.CharField(..., null=True)
type = models.CharField(...)
author = models.CharField(...)
I have a simple class in models.py. In admin I would like to hide title of the book (in book details form) when type of the saved book is 1.
How do this in a simplest way?
For Django > 1.8 one can directly set the fields to be excluded in admin:
class PostCodesAdmin(admin.ModelAdmin):
exclude = ('pcname',)
Hidden fields are directly defined in Django's ORM by setting the Field attribute: editable = False
e.g.
class PostCodes(models.Model):
gisid = models.IntegerField(primary_key=True)
pcname = models.CharField(max_length=32, db_index=True, editable=False)
...
However, setting or changing the model's fields directly may not always be possible or advantegous. In principle the following admin.py setup could work, but won't since exclude is an InlineModelAdmin option.
class PostCodesAdmin(admin.ModelAdmin):
exclude = ('pcname',)
....
A solution working at least in Django 1.4 (and likely later version numbers) is:
class PostCodesAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(PostCodesAdmin, self).get_form(request, obj, **kwargs)
del form.base_fields['enable_comments']
return form
For the admin list-view of the items, it suffices to simply leave out fields not required:
e.g.
class PostCodesAdmin(admin.ModelAdmin):
list_display = ('id', 'gisid', 'title', )
You are to create admin.py in your module (probably book)
class BookAdmin(admin.ModelAdmin):
list_display = ("pk", "get_title_or_nothing")
In Book class:
class Book:
...
def get_title_or_nothing(self):
if self.type == WEIRD_TYPE:
return ""
return self.title
UPDATED:
class BookAdmin(admin.ModelAdmin):
list_display = ("pk", "get_title_or_nothing")
def get_form(self, request, obj=None, **kwargs):
if obj.type == "1":
self.exclude = ("title", )
form = super(BookAdmin, self).get_form(request, obj, **kwargs)
return form
I tried to override get_form() function but some mix up errors occur when I switch in different records. I found there is a get_exclude() function we can override.
Use:
class BookAdmin(admin.ModelAdmin):
def get_exclude(self, request, obj=None):
if obj and obj.type == "1":
# When you create new data the obj is None
return ("title", )
return super().get_exclude(request, obj)
class BookAdmin(admin.ModelAdmin):
exclude = ("fieldname",) # hide fields which you want
Apropos #Lorenz #mrts answer
with Django 2.1 I found that exclude does not work if the field is already specified via fields = .
In that case you may use
self.fields.remove('title')
fields will have to be defined as a list [] for this to work
If you want to maintain the value in the form (for example set a value, i.e. user, based on the request) and hide the field, you can change the widget to forms.HiddenInput():
from django import forms
...
def get_form(self, request, obj=None, **kwargs):
"""Set defaults based on request user"""
# update user field with logged user as default
form = super().get_form(request, obj, **kwargs)
form.base_fields["user"].initial = request.user.id
form.base_fields["user"].widget = forms.HiddenInput()
return form
Here is a working example
class BookAdmin(admin.ModelAdmin):
def get_fieldsets(self, request, obj):
if obj is None:
return [
(
None,
{'fields': ('type', 'description',)}
)
]
elif request.user.is_superuser:
return [
(
None,
{'fields': ('type', 'status', 'author', 'store', 'description',)}
)
]
else:
return [
(
None,
{'fields': ('type', 'date', 'author', 'store',)}
)
]

Extending Django Admin for data import

I'm trying to build an import feature/form in the django admin interface for a specific model.
I have already found the following question on Stackoverflow, however as I am new to django, I have not been able to wire it all up. Import csv data into database in Django Admin
I guess I understand how to work with Django objects and how to use the CSV reader module, but I have a heck of a time putting it all together in Django.
what i tried so far is this:
models.py
class RfidTag(models.Model):
"""
Available RFID-Tags from Importfile
"""
system = models.DecimalField(
_('system'),
max_digits=4,
decimal_places=0,
)
tagId = models.DecimalField(
_('tag ID'),
max_digits=4,
decimal_places=0,
)
serial = models.CharField(
_('serial'),
max_length=10,
)
# forms.py #
class RfidImport(forms.ModelForm):
file_to_import = forms.FileField()
class Meta:
model = RfidTag
fields = ("file_to_import",)
def save(self, commit=False, *args, **kwargs):
form_input = RfidImport()
file_csv = self.cleaned_data['file_to_import']
csv.register_dialect('excel-new', delimiter=';', quoting=csv.QUOTE_NONE)
records = csv.reader(file_csv, dialect='excel-new')
for line in records:
self.system = line[0]
self.tagId = line[1]
self.serial = line[2]
form_input.save()
datafile.close()
admin.py
class RfidTagAdmin(admin.ModelAdmin):
list_display = ('system','tagId','serial')
actions = ['import_tags']
def get_urls(self):
urls = super(RfidTagAdmin, self).get_urls()
my_urls = patterns('',
(r'^import/$', self.admin_site.admin_view(import_tags))
)
return my_urls + urls
def import_tags(self, request, queryset):
return HttpResponseRedirect("./import")
import_tags.short_description = "Import new RFID tags"
pass
admin.site.register(RfidTag, RfidTagAdmin)
views.py
#staff_member_required
def import_tags(request):
if request.method == "POST":
form = RfidImport(request.POST, request.FILES)
if form.is_valid():
form.save()
success = True
context = {"form": form, "success": success}
return HttpResponseRedirect("../")
else:
form = RfidImport()
context = {"form": form}
return HttpResponseRedirect("../")
My question is, is admin action actually the right way? Is there a better way to achieve what I am trying? And how do I wire this up? I have yet to see the form, after I select the import action and click "go".
The admin is the right way, however i wouldn't be using an action for this, those are designed to function over a list of objects and you don't need that. For this case simply extend the admin/index.html template and add an href to your view. After that you create a normal form in which you do your processing

Manipulating Data in Django's Admin Panel on Save

Ok, so here's the skinny:
# models.py
class Article( models.Model ):
title = models.CharField( max_length = 255 )
author = models.ForeignKey( User )
published_at = models.DateTimeField( auto_now_add = True )
body = models.TextField( )
def __unicode__( self ):
return self.title
# admin.py
from hpccoe.news.models import Article
from django.contrib import admin
from django import forms
from django.forms import widgets
class ArticleAdminForm( forms.ModelForm ):
title = forms.CharField( max_length = 255, required = True )
body = forms.CharField( required = True, widget = widgets.Textarea )
class ArticleAdmin( admin.ModelAdmin ):
fields = [ 'title', 'body' ]
form = ArticleAdminForm
admin.site.register( Article, ArticleAdmin )
As you can see, I'm omitting the author field in the Admin form. I want to, somehow, auto-fill this before it's saved. How do I make that happen? I've been on Google for the last hour to no avail.
Thanks in advance.
From the Django Docs:
ModelAdmin.save_model(self, request, obj, form, change)
The save_model method is given the HttpRequest, a model instance, a ModelForm instance and a boolean value based on whether it is adding or changing the object. Here you can do any pre- or post-save operations.
For example to attach request.user to the object prior to saving:
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super().save_model(request, obj, form, change)
class ArticleAdmin( admin.ModelAdmin ):
fields = [ 'title', 'body' ]
form = ArticleAdminForm
def save_model(self, request, obj, form, change):
obj.author = "name Here"
obj.save()