Display choicefield in admin as input field - django

I have a database of maybe 100 users that have each 5-10k products linked to them. In the admin panel loading that page is really slow because of the many products. So what I want to do is replacing it with a regex or at least a number input field that does not preload all the products:
models:
class Store(models.Model):
name = models.CharField("name", max_length = 128)
user = models.OneToOneField(User, on_delete = models.CASCADE, )
testproduct = models.Foreignkey(Product, on_delete = models.CASCADE, null = True, blank = True)
class Product(models.Model):
data = models.JSONField()
number = models.PositiveIntegerField()
store = models.ForeignKey(Store, on_delete = models.CASCADE)
admin:
class StoreAdmin(admin.ModelAdmin):
list_display = ["name", ...]
raw_id_fields = ["testproduct", ...]
This way I get an id input field on the admin page:
Is there any way I can make it a regex field, so I can search through the data attribute of my products as well as the number attribute?

I think, best way to achieve your goal is to add extra field to your model admin form and override save method for it, try this:
#admin.py
from django import forms
class StoreAdminModelForm(forms.ModelForm):
regex_field = forms.CharField()
def save(self, commit=True):
product_regex = self.cleaned_data.get('regex_field', None)
instance = super(StoreAdminModelForm, self).save(commit=commit)
# put some more regex validations here
if len(product_regex) > 0:
product = Product.objects.filter(data__regex=product_regex).first()
if product:
instance.testproduct = product
else:
raise forms.ValidationError('Product with data satisfying regex was not found!')
if commit:
instance.save()
return instance
class Meta:
model = Store
fields = '__all__'
class StoreAdmin(admin.ModelAdmin):
form = StoreAdminModelForm
raw_id_fields = ["testproduct", ]
fields = ('name ', 'user', 'testproduct', 'regex_field')
readonly_fields = ('user', )
So, the result - you will have special field for regex expression and you will try to get testproduct based on that field (without touching raw_id_field)

Related

Display admin TabularInline in list_display

I have two models, which are linked reverse by foreign key from my admin point of view:
class Product(models.Model):
name = models.CharField("name", max_length = 128)
class Store(models.Model):
store_id = models.PositiveIntegerField(unique = True)
product = models.ForeignKey(Product, on_delete = models.CASCADE, null = True, blank = True)
And I have an admin view where I want to display the store_id of each product it is available in list_display.
I ask because I found TabularInline - my apporach:
class StoreInline(admin.TabularInline):
model = Store
readonly_fields = ['store_id', "product"]
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ["name",]
inlines = [StoreInline,]
But how would i be able to display the store_id value in list_displays using the Inlines method?
--- workaround (this is only a motviation for my question above), not a solution ---
I worked around by creating a custom method but, I feel like from reading (1, 2, 3) that I have solved it "by hand" and not using a path Django already has. This works:
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ["name", "get_stores"]
def get_stores(self, obj):
return [s.store_id for s in Store.objects.filter(product = obj)]

Pass request user to modelform in Django

I have a model where is save one of the groups of the user in my model.
Because user can be member of different groups i need to update the Selection field in my Form depending on the request.user.groups.all() queryset.
I tried to pass the initial variable to my form but this was ignored.
Any hint how to deal with this problem?
EDIT:
My view:
form = CoronaEventModelForm(request.POST or None, initial={'group': request.user.groups.all()})
my model:
user = models.ForeignKey(curr_User, default=1, null=True, on_delete=models.SET_DEFAULT, related_name='group')
group = models.ForeignKey(Group, on_delete=models.CASCADE)
my form:
class CoronaEventForm(forms.Form):
user = forms.CharField()
group = forms.CharField()
title = forms.CharField()
description = forms.CharField()
slug = forms.SlugField()
event_date = forms.DateField()
class CoronaEventModelForm(forms.ModelForm):
class Meta:
model = CoronaEvent
fields = ['group', 'title', 'description', 'slug', 'event_date']
it works with normal text fields but not with the group field.
solved it by adding a init function to my form and passing the object during instantiation of the form.

How to Auto Invite all users of selected Group using Django admin inlines form?

I am working on an application where currently, I have
1) Staff Model is connected to User model via OneToOne Relationship and can have more than one Group.
2) Meeting model can also assigned to many Group.
3) RSVPinline is a part with MeetingAdmin as a inline form.
Here i was trying to automatically ADD all 'Staff' associated in Selected Groups in django admin form while creating Meetings.
I have tried save_model to add current user in meeting's 'creator' field.
models.py
class Group(models.Model):
name = models.CharField(max_length=200)
class Staff(models.Model):
fullname = models.CharField(max_length = 250,verbose_name = "First Name")
group = models.ManyToManyField(Group, blank=False,verbose_name = "Meeting Group") # protect error left to add
is_active = models.BooleanField(default=True)
user = models.OneToOneField(User, on_delete=models.CASCADE,null=True, blank=True,verbose_name = "Associated as User") # protect error left to add
left_date = models.DateField(null=True, blank=True,verbose_name = "Resigned Date")
class Meeting(models.Model):
title = models.CharField(_('Title'), blank=True, max_length=200)
start = models.DateTimeField(_('Start'))
group = models.ManyToManyField(Group, blank=False,verbose_name = "Meeting Group") # protect error left to add
location = models.ForeignKey(Location, blank=False,verbose_name = "Location",on_delete=models.CASCADE) # protect error left to add
class RSVP(models.Model):
meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE)
responder = models.ForeignKey(User, editable=True, on_delete=models.CASCADE, null=True, blank=True,verbose_name = "Attendees", related_name='guest')
response = models.CharField(max_length = 20, choices= MEETING_RSVP, default='No response', verbose_name = "Status",null=True, blank=True)
admin.py
class RSVPInline(admin.TabularInline):
model = RSVP
extra = 0
class MeetingAdmin(admin.ModelAdmin):
form = MeetingForm
list_display = ('title', 'location', 'start','creator' )
inlines = [RSVPInline, TaskInline]
#Currently using save_model to automatically add current user as a creator
def save_model(self, request, obj, form, change):
obj.creator = request.user
super().save_model(request, obj, form, change)
My pseudo code is:
grouplist = Get group's list from submitted MeetingForm
stafflist = Staff.objects.filter(department__in =grouplist).values_list('id', flat=True).distinct()
Add to RSVPInline:
values = list(for staff in stafflist:
'responder' = staff
'meeting' = 'meeting from MeetingForm'
'response' = has a default value in model
bulk_create() RSVPInline with values
You can extend save_related() ModelAdmin method to perform additional actions after form object (Meeting) and its Inlines (RSVPs, if present in submitted form) are saved:
class MeetingAdmin(admin.ModelAdmin):
...
def save_related(self, request, form, formsets, change):
# call original method - saves Meeting and inlines
super(MeetingAdmin, self).save_related(request, form, formsets, change)
# get this form Meeting
obj = form.instance
# get Staff members of this meeting groups
# and we can exclude ones already having
# RSVP for this meeting
stafflist = Staff.objects.filter(
group__in=obj.group.all()
).exclude(
user__guest__meeting=obj
)
rsvps = list(
RSVP(responder=staff.user, meeting=obj)
for staff in stafflist
)
# calls bulk_create() under the hood
obj.rsvp_set.add(*rsvps, bulk=False)
** Few possibly useful notes:
group field may be better to be called groups as it represents ManyToMany relation and returns multiple objects
related_name represents relation from the related object back to this one so it may be more logical to use something like invites instead of guest

Understanding how Models and Forms affect one another and database

I am creating form for users to input information too. I then want to save that information to a database and be able to extract information.
I am having difficulty however when I try to create a drop down list and multiple selection list. For example say I define a model as in my models.py file as such:
gi_category = models.CharField(max_length=4, choices=GI_NOGI)
Where I have GI_NOGI specified as
GI_NOGI = (('GI','GI'),('NOGI','NO-GI'))
My forms.py looks something like this
class RegisterForm(forms.ModelForm):
weightclass = forms.ChoiceField(choices=WEIGHTCLASS, required=True,label='Weight Class')
gi_category = forms.MultipleChoiceField(choices=GI_NOGI, label='Contest You Intend to Enter', widget=forms.CheckboxSelectMultiple(), required=True)
class Meta:
model = Register
fields = ['name', 'age', 'birthdate', 'address', 'city', 'state', 'zip',
'phone', 'email']
labels = {'name':'Name', 'age':'Age on April 21', 'birthdate':'Birth date',
'address':'Address', 'city':'City', 'state':'State', 'zip':'Zip',
'phone':'Phone', 'email':'Email'}
widgets = {
'birthdate':forms.TextInput(attrs={'placeholder': 'MM/DD/YYYY'}),
'phone':forms.TextInput(attrs={'placeholder': '(###)-###-####'})}
Now I believe I am overwriting the gi_category and weightclass from models somehow because I cant access their respective values in the database. I don't know how to create a SelectMultiple any other way than I had I did(If this is problem any insight would be great.
I am wondering what I am doing wrong?
Also on a related note I want to have a database value for gi_category in which it can either have values of 'NO-GI' or/and 'GI'
I am saving the form values using this views.py
WEIGHTCLASS = (
('MFYW','Men Fly Weight(129 lbs. & Under)'),
('MBW','Men Bantan Weight(130 lbs. to 139.9 lbs)'),
('MFEW','Men Feather Weight(140 lbs. to 149.9 lbs.)')
)
GI_NOGI = (('GI','GI'),
('NOGI','NO-GI')
)
class Register(models.Model):
"""register some text"""
name = models.CharField(max_length=200)
age = models.CharField(max_length=3)
email = models.EmailField(max_length=200)
birthdate = models.DateField()
address = models.CharField(max_length=200)
city = models.CharField(max_length=200)
state = models.CharField(max_length=200)
zip = models.CharField(max_length=5)
phone = models.CharField(max_length=14)
weightclass = models.CharField(max_length=4, choices=WEIGHTCLASS)
gi_category = models.CharField(max_length=4, choices=GI_NOGI)
def __str__(self):
"""Return a string represenation of the model."""
return self.weightclass
Thanks

Validation fails on a select box whose contents are added by an Ajax call

This question is related to this one
Remove all the elements in a foreign key select field
I had a foreign key field in my model which was getting pre-populated by its data and I wanted the select list to be empty. I did achieve that but the validation fails when I submit the form.
The error says "Select a valid choice option. 1 is not one of the available choices).
These are my models
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
serving_size = models.ForeignKey(ServingSize)
quantity = models.IntegerField()
order = models.IntegerField()
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
class RecipeIngredientForm(forms.ModelForm):
serving_size = forms.ChoiceField(widget=forms.Select())
class Meta:
serving_size = forms.ChoiceField(widget=forms.Select())
model = RecipeIngredient
fields = ('ingredient', 'quantity', 'serving_size')
widgets = {
'ingredient': forms.TextInput(attrs={'class' : 'recipe_ingredient'}),
'quantity': forms.TextInput(),
'serving_size' : forms.Select(attrs={'class' : 'ddl'}),
}
I get an error on the third line
recipeIngredients = models.RecipeIngredientFormSet(request.POST)
print(recipeIngredients.errors)
objRecipeIngredients = recipeIngredients.save(commit=False)
I want the select box to be empty because it gets populated by an ajax call. Any ideas what to do so the model passes the validation?
EDIT
Serving Size Model
class ServingSize(models.Model):
name = models.CharField(max_length = 255)
unit = models.CharField(max_length = 125)
food_group = models.ForeignKey(FoodGroup)
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
objects = models.Manager()
dal_objects = ServingSizeManager()
def __unicode__(self):
return self.name;
First, why do you have serving_size in the Meta class?
I would use an extra field in the ModelForm and leave out serving_size field altogether.
class RecipeIngredientForm(forms.ModelForm):
mycustomfield = forms.ChoiceField(widget=forms.Select())
class Meta:
model = RecipeIngredient
exclude = ('serving_size', 'created', 'updated') #etc
Then in the view I would manipulate the form to assign a valid ServingSize to the serving_size field.
[EDIT]
Alright, your actual implementation will depend on what you are pulling through ajax and how. But see the following code: -
Your form: -
class CustomRecipeIngredientForm(forms.ModelForm):
recipe = forms.ModelChoiceField( Recipe.objects.all(),
widget=forms.Select(attrs={'class':'customclass',}))
ingredient = forms.ModelChoiceField( Ingredient.objects.all(),
widget=forms.Select(attrs={'class':'recipe_ingredient',}))
my_custom_serving_size_field = forms.ChoiceField(widget=forms.Select(attrs={'class':'ddl',}))
quantity = forms.IntegerField()
order = forms.IntegerField()
class Meta:
model = RecipeIngredient
exclude = ('serving_size', 'created', 'updated',)
Pull your data through ajax into the my_custom_serving_size_field
Your view: -
def my_view(request):
if request.method == 'POST':
form = CustomRecipeIngredientForm(data=request.POST)
if form.is_valid():
new_recipe_ingredient = form.save(commit=False)
new_recipe_ingredient.serving_size = ServingSize.objects.get(pk=form.cleaned_data['my_custom_serving_size_field'])
new_recipe_ingredient.save()
return HttpResponseRedirect(reverse('redirect_to_wherever'))
else:
form = CustomRecipeIngredientForm()
return render_to_response('path/to/my_template.html', {'form': form}, context_instance=RequestContext(request))
Of course, your ServingSize.objects.get() logic will depend on what your are pulling through ajax and how. Try something along these lines and let us know.
Looks like you want a ModelChoiceField, which
Allows the selection of a single model
object, suitable for representing a
foreign key