Add multiple records at once in django admin panel - django

I have following setup.
from django.db import models
from django.contrib.auth.models import User
class Event(models.Model):
name = models.CharField(max_length=64)
date = models.DateField()
ATTENDANCE_CHOICES = (
('A','Attending'),
('N','Absent'),
('L','Taken ill'),
)
class Attendance(models.Model):
student = models.ForeignKey(User)
event = models.ForeignKey(Event)
status = models.CharField(max_length=1, choices=ATTENDANCE_CHOICES)
In a nutshell: Students(User) attend or doesn't attend classes(Event), this is registered by Attendance.
Problem is adding those attendance records one at a time.
What I am looking for is a way to provide form for each class(each Event object) with list of all students and attendance status radio buttons or drop downs next to them.
Something like this:
http://i.imgur.com/jANIZ.png
I have looked at many discussions about multiple/bulk record insertion via django admin and am beginning to wonder is this even possible with django admin or do I have to create such form from scratch? Either way, what would be the best (most django-ish) approach?

"Is this even possible?" It's possible right out of the box.
Look into the django Admin app, Inlines, ModelForms, and the RadioSelect widget.
class MyForm(forms.ModelForm):
class Meta:
model = Attendance
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs
self.fields['status'].widget = forms.RadioSelect(choices=self.fields['status'].choices)
class AttendanceInline(admin.TabularInline):
model = Attendance
form = MyForm
class EventAdmin(admin.ModelAdmin):
inlines = [AttendanceInline]
def save_model(self, request, obj, form, change):
obj.save()
for user in User.objects.all():
obj.attendance_set.create(user=user, status='')
# you should consider a null field or a possible choice for "Undecided"
admin.site.register(Event, EventAdmin)

Related

Django ModelChoice Field Conditional ForeignKey

I've surfed most of the afternoon and have been at this particular quandry for a while.
I am trying to figure out how to essentially present a foreign key as a dropdown choice if the user has driven that type of car. For example purposes and to keep this as easy as possible...
Let's say I have aCars, Manufacturers and a UserProfile model.
I have a model for Cars as so...
class Cars(models.Model):
car_name = models.CharField(max_length=80)
class = models.ForeignKey(Manufacturer,null=True,on_delete=models.DO_NOTHING,related_name='car_manufacturer')
I have a model for Manufacturers as so...
class Manufacturers(models.Model):
manu_name = models.CharField(max_length=80)
Then I have a UserProfile model....
class Userprofile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
user_name = models.CharField(max_length=80)
car_owned = models.ForeignKey(Car,null=True,on_delete=models.DO_NOTHING,related_name='car_owned')
All good so far...
I have a view where I am listing all of the Manufacturers and this works fine as well. It shows all of the manufacturers that I would expect in the form view below.
class ManufacturerForm(forms.Form):
dropdown = forms.ModelChoiceField(queryset=Manufacturer.objects.all())
def __init__(self, *args, **kwargs):
super(ManufacturerForm, self).__init__(*args, **kwargs)
self.fields['dropdown'].widget.attrs['class'] = 'choices1'
self.fields['dropdown'].empty_label = ''
I'm using the FORMVIEW below to display the form...
class ManufacturerView(LoginRequiredMixin,FormView):
form_class = ManufacturerForm
template_name = 'Directory/HTMLNAME.html'
def get_form_kwargs(self):
kwargs = super(ManufacturerView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
manufacturer = form.cleaned_data['dropdown']
return HttpResponseRedirect(reverse("NAME:manufacturer",kwargs={'pk':manufacturer.pk}))
This all works fine. However, I can't figure out how to limit the manufacturer dropdown to only the cars the user has driven. I'm trying to essentially limit the dropdown display to only the manufacturers that are pertinent to the cars the user has owned based on their profile. I've researched reverse look ups and have also tried something similar to what is outlined below to solve my problem...
class ManufacturerForm(forms.Form):
dropdown = forms.ModelChoiceField(queryset=Manufacturer.objects.filter(car_manufacturer=1)
def __init__(self, *args, **kwargs):
super(ManufacturerForm, self).__init__(*args, **kwargs)
self.fields['dropdown'].widget.attrs['class'] = 'choices1'
self.fields['dropdown'].empty_label = ''
But this obviously only gives me record 1 for the Manufacturer model. I am trying to figure out how to display only the records that are relevant to an individual user based on their car_owned data. I can list all of the manufacturers and then just display the records that are relevant in a ListView, but I am trying to limit the dropdown to only where there are relevant records in a ListView. Thanks in advance for any thoughts.
You missed just couple of points:
1) Pass UserProfile as kwargs['user']:
kwargs['user'] = UserProfile.objects.get(user=self.request.user)
2) Add user parameter in form's __init__ signature and override dropdown.queryset there:
class ManufacturerForm(forms.Form):
dropdown = forms.ModelChoiceField(queryset=Manufacturer.objects.all())
def __init__(self, user, *args, **kwargs):
super(ManufacturerForm, self).__init__(*args, **kwargs)
self.fields['dropdown'].widget.attrs['class'] = 'choices1'
self.fields['dropdown'].empty_label = ''
self.fields['dropdown'].queryset = Manufacturer.objects.filter(car_manufacturer__car_owned=user)
Also I would like to recommend to rewrite your Car - User relationship to ManyToMany. If I understand correctly your message, User can have multiple cars:
limit the dropdown display to only the manufacturers that are
pertinent to the cars the user has owned
Also if I understand correctly, you want to track cars that user used to have (but doesn't have anymore).
If you rewrite Car - User relationship, then you won't probably have any reason to keep UserProfile model only to hold additional username. If so, your models.py should look like this:
class Manufacturer(models.Model):
name = models.CharField(max_length=80)
class Car(models.Model):
name = models.CharField(max_length=80)
klass = models.ForeignKey(Manufacturer, null=True, on_delete=models.DO_NOTHING, related_name='car_manufacturer')
owners = models.ManyToManyField(User, through='Ownership')
class Ownership(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
car = models.ForeignKey(Car, on_delete=models.CASCADE)
active = models.BooleanField(default=True) # True if user owns the car at the moment
I think it all hinges on your order and relationships between models, perhaps try adding in a ManyToMany relation between Manufacturer and Car, so one manufacturer can make many cars:
class Manufacturer(models.Model):
name = models.CharField(max_length=80)
car = models.ManyToManyField(Car)
Then it may be a case of doing something such as:
qs = Manufacturer.objects.all(car=user.car_owned)
dropdown = forms.ModelChoiceField(qs)
And in your views.py file:
form = ManufacturerForm(request.POST, user=request.user)
(You may need to look up if the above is valid, as I'm not sure if Forms can have the request object passed in).
Disclaimer: I see you're using a class based view...so I may need to tweak the above.

selective display of model choices in django

In my django application I have a model field called 'status'. In one of the forms to get data for this field, I only want to display a subset of all choices available in the model. Is there a way to remove a choice from a form? I need the removed choice in the database and the admin interface where I can select it.
status = models.CharField(STATUS_FIELD_NAME, choices=STATUS_CHOICES,
default=STATUS_DEFAULT,
max_length=3)
You could define the subset of choices in your form:
class YourForm(forms.ModelForm):
SUBSET_CHOICES = (
(YourModel.CHOICE_ONE, _('First choice')),
(YourModel.CHOICE_TWO, _('Second choice')),
)
class Meta:
model = YourModel
fields = ['choice', ]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['choice'].choices = self.SUBSET_CHOICES

Using a SELECT field for a OneToMany field

let's say I've the following very simple models:
class Customer(models.Model):
name = models.CharField(max_length=50)
class Probe(models.Model):
OwnerInfo = models.CharField(max_length=50)
comments = models.CharField(max_length=50)
customer = models.ForeignKey(Customer, null=True, blank=True)
I've been able to add an InLine to the Admin gui, but I'd like to use a SELECT component, so I can just select several Probes and assign them to the Customer. From this question:
one-to-many inline select with django admin
I know thanks to Luke's answer (last one) that I should create a custom Form and assign it to my ModelAdmin.form but I can not wonder how to tie it all together to make it work.
May anyone help?
Thanks a lot in advance.
OK, I came a step further, and now I've the field added to the Form, like this:
from django.contrib import admin
from django import forms
from web_gui.models import Probe, Customer, Firmware
class CustomerForm(forms.ModelForm):
probes = forms.ModelMultipleChoiceField(queryset=Probe.objects.all())
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['probes'].initial = [p.pk for p in Probe.objects.filter(customer_id=self.instance.pk)]
class Meta:
model = Customer
class CustomerAdmin(admin.ModelAdmin):
form = CustomerForm
admin.site.register(Probe)
admin.site.register(Customer, CustomerAdmin)
admin.site.register(Firmware)
but the initial values specified through "initial" are not being selected. What's wrong now? I assume that next will be to override the save() method to set the Probes on the Customer, am I right?
This is the best solution I've came up with. Let me know if there is any other better way of achieving this:
from django.contrib import admin
from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from web_gui.models import Probe, Customer, Firmware
class CustomerForm(forms.ModelForm):
probes = forms.ModelMultipleChoiceField(queryset = Probe.objects.all(), required=False)
probes.widget = FilteredSelectMultiple("Probes",False,attrs={'rows':'10'})
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['probes'].initial = [p.pk for p in Probe.objects.filter(customer_id=self.instance.pk)]
def save(self, force_insert=False, force_update=False, commit=True):
c = super(CustomerForm, self).save(commit=False)
c.probe_set = self.cleaned_data['probes']
c.save()
return c
class Meta:
model = Customer
class CustomerAdmin(admin.ModelAdmin):
form = CustomerForm
admin.site.register(Probe)
admin.site.register(Customer, CustomerAdmin)
admin.site.register(Firmware)

Django, Filter the set presented in a many to many modelform by currently logged in user

I know it's there somewhere but I can't find it.
So I have a 'category' model and a 'book' model which has a many to many to 'category'. When creating a new book in a modelform, all the categories are presented to the user to assign to the book. In that case I want only the categories created by the current user to show up in that field, not all the categories.
What's the best approach?
Assuming your model like:
class Category(models.Model):
....
creator = models.ForeignKey(User)
class Book(models.Model):
...
categories = models.ManyToManyField(Category)
Assuming your form like:
class BookForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
current_user = kwargs.pop('user')
super(BookForm, self).__init__(*args, **kwargs)
self.fields['categories'].queryset = Categories.objects.filter(creator=current_user)
So, you need to overide __init__ of your form, pass the current user to this form. And then set a queryset attribute on the ManyToManyField you want.
Your view:
#GET request
book_form = BookForm(user=request.user)
#POST request
book_form = BookForm(data=request.POST, user=request.user)

How to create several custom User models and be able to customize their admin forms?

EDIT I reformulated the question here
I want to create several custom user models extending django.contrib.auth.models.User, with the following features:
some specific fields
a nice admin, i.e., for each model, an admin form that I can customize easily (eg. show/hide some fields, both from the parent django.contrib.auth.models.User and the child model).
I almost managed to do it with the code below, but I still have an issue: the password is cleared every time I want to modify an instance of MyUser1 or MyUser2 from the admin.
Is it the best way to do it? If so, how can I fix this cleared password issue?
models.py
from django.db import models
from django.contrib.auth.models import User
class MyUser1(User):
#add more fields specific to MyUser1
class MyUser2(User):
#add more fields specific to MyUser2
admin.py
class MyUser1AdminForm(forms.ModelForm):
class Meta:
model = MyUser1
def __init__(self, *args, **kwargs):
super(MyUser1AdminForm, self).__init__(*args, **kwargs)
self.fields['password'].widget = forms.PasswordInput()
def save(self, commit=True):
user = super(MyUser1AdminForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user
class MyUser1Admin(admin.ModelAdmin):
form = MyUser1AdminForm
admin.site.register(MyUser1, MyUser1Admin)
# same for MyUser2
if you whant for user to enter pass - add new field for pass and check it before commit
class MyUser1AdminForm(forms.ModelForm):
check_pass = forms.CharField(label="Password",widget = forms.PasswordInput(), required=True)
class Meta:
model = MyUser1
exclude = ('password',)
def save(self, commit=True):
user = super(MyUser1AdminForm, self).save(commit=False)
if commit and user.check_password(self.cleaned_data["check_pass"]):
user.save()
return user