filtering dropdown values in django admin - python

class Foo(models.Model):
title = models.TextField()
userid = models.IntegerField()
image = models.CharField(max_length=100)
def __unicode__(self):
return self.title
class Bar(models.Model):
foo = models.ForeignKey(Foo, related_name='Foo_picks', unique=True)
added_on = models.DateTimeField(auto_now_add=True)
In Django admin add_view:
def add_view(self, *args, **kwargs):
self.exclude = ("added_on",)
self.readonly_fields = ()
return super(Bar, self).add_view(*args, **kwargs)
So, Field shows in the admin add view is foo Which is a drop down list and shows all the titles. Some title of Foo remains empty or ''. So, drop down list have lots of empty value because it title is empty. I want to filter out those empty values.

You can provide your own form for ModelAdmin, with custom queryset for foo field.
from django import forms
from django.contrib import admin
#Create custom form with specific queryset:
class CustomBarModelForm(forms.ModelForm):
class Meta:
model = Bar
fields = '__all__'
def __init__(self, *args, **kwargs):
super(CustomBarModelForm, self).__init__(*args, **kwargs)
self.fields['foo'].queryset = Foo.objects.filter(title__isnull=False)# or something else
# Use it in your modelAdmin
class BarAdmin(admin.ModelAdmin):
form = CustomBarModelForm
Something like this...
docs

for django 1.6:
For foreign key:
https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#ModelAdmin.formfield_for_foreignkey
class MyModelAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "title":
kwargs["queryset"] = Foo.objects.filter(title__isnull=False)
return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

I stumbled across this question when looking for a solution to filter dropdown options on the fly in the admin interface based on the selection in another field -- not based on a pre-filtered list at page load. The solution I found was this library: https://github.com/digi604/django-smart-selects which is an app that uses ajax calls and allows chain filtering to multiple levels. Works like a charm for me. -HTH

You could subclass your own model.ModelAdmin and create a custom field for your ChoiceField...
class CustomForm(model.ModelForm):
class Meta:
model = Foo
foo = forms.ChoiceField(widget=forms.Select, initial=self.foo_queryset)
def foo_queryset(self):
return Foo.objects.filter(xy)...
class FooAdmin(model.ModelAdmin):
form = CustomForm

Related

Error while saving foreign key data in database using Django Model Form

I have two Models for my Project, 1. Category Model and 2. Course Model
Course Model has a Foreign Key reference with my Category Model as shown below.
class Category(models.Model):
categoryname = models.CharField(max_length=200,null=True,blank=True, default="")
class Courses(models.Model):
coursename = models.CharField(max_length=200,null=True,blank=True, default="")
course_category = models.ForeignKey(Category, related_name="courses", blank=True,null=True,on_delete=models.CASCADE)
logo = models.ImageField(upload_to='courselogos', null=True, blank=True)
Initially I was using HTML form and will be able to save the Course data under a Particular Category to the database as:
def add_course(request):
if request.method == 'POST':
course_name = request.POST.get('coursname')
categoryid = request.POST.get('category_id')
category = Category.object.get(id=category_id)
course_logo = request.FILES.get('logo')
course = Courses(coursename=course_name, course_category=category, logo= course_logo)
course.save()
return redirect('/all_category')
Later I decided to move on using Django Model forms and I tried to implement the code as follows
class AddCourseForm(forms.ModelForm):
class Meta:
model = Courses
fields = ('coursename', 'course_category', 'logo')
widgets = {
'coursename' : forms.TextInput(attrs={'class':'form-control'}),
}
def __init__(self, *args, **kwargs):
category_id = kwargs.pop('category_id',1)
super(AddCourseForm, self).__init__(*args, **kwargs)
self.fields['course_category']=forms.ModelChoiceField(widget=forms.TextInput(), queryset=Category.objects.filter(id=category_id))
Later in the view I have saved the data as
def add_course(request):
if request.method == 'POST':
addcourse = AddCourseForm(request.POST, request.FILES)
if addcourse.is_valid():
addcourse.save()
return redirect('/all_category')
On my HTML page I am passing the input to the 'course_category' inputfield as 1,2,3....etc as the category_id value
I have rendered the field in the form as
{{form.course_category}}
On Submitting the form when my 'course_category' inputfield has value as 1, it saves the data to the database but when the inputfield value is 2 then it is not even entering to the if condition of addcourse.is_valid() in the view function.
As I'm new the Django I'm not able to find the right way to get the ForeignKey value dynamically save the data in reference to that Category. Also I want to populate the same data back to the form in case of edit.
Please guide, thanks in advance.
After debugging the Code a little bit, I modified the init function in the AddCourseForm class as mentioned below that solved my issue but I am not it is the right way to do this or not
def __init__(self, *args, **kwargs):
category_id = None
for key in args[0:1]:
category_id = args[0].get('course_category')
super(AddCourseForm, self).__init__(*args, **kwargs)
self.fields['course_category']=forms.ModelChoiceField(widget=forms.TextInput(), queryset=Category.objects.filter(id=category_id))
I don't think doing this should be that difficult, here is how you would set the course_category options in the form normally:
# forms.py
class AddCourseForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
self.course_categories = Category.objects.all()
super(AddCourseForm, self).__init__(*args, **kwargs)
self.fields['course_category'].queryset = self.course_categories
If you want to set a particular category in the form the you can pass an initial value in your view:
# views.py
def add_course(request, pk):
# note: you can pass the category primary key to your view, you need to
# specify this in your URLs and then your template
course_category = Category.objects.get(pk=pk)
form = AddCourseForm(initial={'course_category': course_category})
If you then want to kill all other options entirely, you can use the initial value to set your filter:
# forms.py
class AddCourseForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(AddCourseForm, self).__init__(*args, **kwargs)
self.fields['course_category'].queryset = Category.objects.filter(
pk=self.fields['course_category'].initial)

Creating dropdown menu in django for user created data only in a different class

I'm new to programming and my first language/stack is Python and Django. I have figured out how to create a dropdown menu in my Script form that is pointing to a different class "Patient" but I can't figure out how to only show me data that the current user created. I'm confused if I should set this in my models.py, forms.py or in the views.py? Here is what I have that I think should be working but it is not. (Tried setting in the views.py)
Models.py
class Patient(models.Model):
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
patient_name = models.CharField(max_length=40, unique=True)
def __str__(self):
return self.patient_name
class Script(models.Model):
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE, verbose_name='Primary Patient')
So my patient field is my dropdown and it is looking at the Patient class grabbing the patient name string. I only want patient_name entry's that this user created in the dropdown.
Views.py
class ScriptCreateView(LoginRequiredMixin, CreateView):
model = Script
template_name = 'script_new.html'
success_url = reverse_lazy('script_list')
fields = (
'patient',
'drug_name',
'drug_instructions',
'drug_start_day',
'drug_start_time',
'drug_hours_inbetween',
'drug_num_days_take',
)
#This sets user created fields only??
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(
author=self.request.user
)
#This sets the author ID in the form
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form
)
Forms.py
class ScriptForm(forms.ModelForm):
class Meta:
model = Script
fields = '__all__'
#This is requiring user login for any of these views??
def __init__(self, user=None, *args, **kwargs):
super().__init__(*args, **kwargs)
if user:
self.fields['patient'].queryset = Patient.objects.filter(author=user)
I'm sure it is my lack of experience here but I thought by setting the function def get_queryset in the view that it would only show me user created data. I have googled a bunch and I really can't find the clear answer on this.
In your views.py file initialize form like this please
<form or form_class> = Form(request.POST, user=request.user)
I had to add the last form.fields query below in the view which filtered items only created by "author" which is what I was looking for:
def get_form(self):
form = super().get_form()
form.fields['drug_start_day'].widget = DatePickerInput()
form.fields['drug_start_time'].widget = TimePickerInput()
form.fields['patient'].queryset = Patient.objects.filter(author=self.request.user)
return form

Render dropdown in django view using values from postgres arrayfield

Here is an example from the model:
class Shipment(models.Model):
shipment_id = models.BigAutoField(null=False, primary_key=True)
potential_shipping_dates = ArrayField(models.DateField(), verbose_name='Ship Dates', null=True)
Here is what I'm sort of attempting in my form:
class ShippingForm(forms.Form):
potential_shipping_dates = forms.ModelChoiceField(queryset=Shipment.objects.all())
def __init__(self, *args, **kwargs):
super(ShippingForm, self).__init__(*args, **kwargs)
And here is where my form is added to context:
context['shippingForm'] = ShippingForm(initial=??what_goes_here_maybe??)
My form renders fine but I want to show a dropdown with a date for each option.
Okay this is a bit complex, but I think I understand what you're trying to do, and where you're going wrong.
So you have a Shipment model, and each Shipment instance has a field with a few different potential_shipping_dates.
Say you have 2 shipments:
IN : ship1 = Shipment.objects.first()
OUT:
IN : ship1.potential_shipping_dates
OUT: ['01/01/2021', '02/02/2021']
IN : ship2 = Shipment.objects.last()
OUT:
IN : ship2.potential_shipping_dates
OUT: ['03/03/2021', '04/04/2021']
Now, do you want the dropdown to have all 4 dates as possibilities, and that will select the Shipment?
Or do you want to select a date after selecting the shipment in the form?
^^ Answered in comments
Okay so you will need to pass the instance through to the form:
views.py
# Inherit from Django's UpdateView to have `instance` passed through to the form
class ShippingFormView(UpdateView):
model = Shipment
form_class = ShippingForm
# Or if you don't want to inherit from inherit from UpdateView
class ShippingFormView(Blah):
model = Shipment
form_class = ShippingForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['instance'] = self.get_object()
return kwargs
# Or if you're using function based views
def shipping_form_view(request, pk):
shipment = get_object_or_404(Shipment, pk=pk)
form = ShippingForm(request, instance=shipment)
...
forms.py
class ShippingForm(forms.Form):
potential_shipping_dates = forms.ChoiceField(choices=[])
def __init__(self, *args, instance, **kwargs):
super(ShippingForm, self).__init__(*args, **kwargs)
self.fields['potential_shipping_dates'].choices = ((dt, dt) for dt in instance.potential_shipping_dates)
ModelChoiceFields are used when selecting an object, not an attribute on one.

Django: How to create one Class Based view with two models which have one-to-many relationship?

I want users be able to create a post with multiple images (that's why I have separate models). How am I able to do that using class based views and one template?
models.py
class UserPost(models.Model):
title=models.CharField(max_length=255)
text=models.TextField()
class Image(models.Model):
user_post = models.ForeignKey(UserPost, on_delete=models.CASCADE)
image = models.ImageField(upload_to='images/')
If anyone is struggling with this, I solved it this way.
forms.py
class UploadPostForm(ModelForm):
class Meta:
model=myModels.UserPost
fields=['title','text','category','is_used','price']
class UploadImages(ModelForm):
class Meta:
model=myModels.Image
fields=['image']
views.py
class CompletePost(LoginRequiredMixin,View):
def get(self, request, *args, **kwargs):
post_form=myForms.UploadPostForm()
image_form=myForms.UploadImages()
return render(request,'shop/create_post.html',{'post_form':post_form,'image_form':image_form})
def post(self, request, *args, **kwargs):
post_form=myForms.UploadPostForm(request.POST,request.FILES)
image_form=myForms.UploadImages(request.POST)
if post_form.is_valid() and image_form.is_valid():
user_post_v=post_form.save(commit=False)
images=image_form.save(commit=False)
user_post_v.user_id = self.request.user
user_post_v.save()
for image in request.FILES.getlist('image'):
image_obj = shopModels.Image()
image_obj.user_post = user_post_v
image_obj.image = image
image_obj.save()
transaction.commit()
return redirect('home')

Filter Foreignkey within Inlineform

Hello I cant seem to filter a Foreignkey Dropdown within an Inline form.
These are my classes:
class Author(models.Model):
name = models.CharField(max_length=50)
desc = models.CharField(max_length=50)
class Book(models.Model):
author = models.ForeignKey(Author)
title= models.CharField(max_length=50)
class BookPrio::
author = models.ForeignKey(Author)
book = models.ForeignKey(Book)
prio = models.IntegerField()
my admin.py looks like:
class BookPrioInline(admin.TabularInline):
model = BookPrio
class AuthorAdmin(admin.ModelAdmin):
inlines =(BookPrioInline,)
admin.site.register(Author, AuthorAdmin)
I want the Books dropdown on the BookPrio inline to be filter on the selected Author in the admin panel. But can;t find out how to do it.
Some help would be welcome
I'm a little confused by your question but found it interesting.
You want the author dropdown on the inlines to be the selected author -- so the inline will always only have 1 choice, the current author?
Well, normally you'd use formfield_for_foreignkey
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey
But you have a special case where each inline depends on the object being edited.
I didn't see any easy ways to access the edited objects so I put the formfield_for_foreignkey definition in the change_view, and assigned the inlines from within the view function.
class BookPrioInline(admin.TabularInline):
model = BookPrio
class AuthorAdmin(admin.ModelAdmin):
inlines = (BookPrioInline,)
def change_view(self, request, object_id, extra_context=None):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'book':
kwargs['queryset'] = Book.objects.filter(author__id=object_id)
return super(ItemInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
ItemInline.formfield_for_foreignkey = formfield_for_foreignkey
self.inline_instances = [ItemInline(self.model, self.admin_site)]
return super(AuthorAdmin, self).change_view(request, object_id,
extra_context=extra_context)
admin.site.register(Author, AuthorAdmin)

Categories