Django forms don't get up-to-date from db - django

I have form where I print all of records from table(lets say its 'item' table in database). User can add new items to db using ajax. Data saves to db correct but when I refresh page i don't see new tags in my multi select box.
I thought cache is a problem but it doesn't.
So I have question: Where is a problem that I can see add records correct but when i refresh this same page where every time select all rows from table then i don't see these new records?
I'm using sqlite and it's development server.
Forms.py:
BLANK_CHOICE = (('', '---------'),)
class OrderCreateForm(forms.ModelForm):
tag_from = forms.MultipleChoiceField(label='Tags', choices=OrderItemList.objects.all().values_list('id', 'name'))
tag_to = forms.MultipleChoiceField()
class Meta:
model = Order
fields = ('price', 'client', 'platform')
def __init__(self, request_client_id, *args, **kwargs):
super(OrderCreateForm, self).__init__(*args, **kwargs)
self.fields['platform'].choices = BLANK_CHOICE + tuple(
Platform.objects.filter(client_id=request_client_id).values_list('id', 'name'))
View.py:
#user_passes_test(lambda u: u.is_staff, login_url='/account/login/')
def order_create(request, request_client_id):
dict = {}
dict['form_order'] = OrderCreateForm(request_client_id)
return render(request, 'panel/order/form.html', dict)

The problem is that you are setting the tag_from choices in the field definition, so the choices are evaluated once when the form first loads. You can fix the problem by setting the choices in the __init__ method instead.
class OrderCreateForm(forms.ModelForm):
tag_from = forms.MultipleChoiceField(label='Tags', choices=())
...
def __init__(self, request_client_id, *args, **kwargs):
super(OrderCreateForm, self).__init__(*args, **kwargs)
self.fields['tag_from'].choices = OrderItemList.objects.all().values_list('id', 'name'))
...
Another option would be to use a ModelMultipleChoiceField instead of a regular multiple choice field. With a model multiple choice field, Django will evaluate the queryset each time the form is initialised.
class OrderCreateForm(forms.ModelForm):
tag_from = forms.MultipleChoiceField(label='Tags', queryset=OrderItemList.objects.all())

Related

Django Form fields changing after page refresh

The first time I open this ModelAdmin's /add page, all the fields of the ServiceProvider model are displayed, although I specified with self.fields the fields that should be displayed.
When pressing F5 to reload the page, the unsolicited fields do not appear. I suspected cache, but disabling the caches did not cause changes. The problem with loading all fields is that it also does some heavy querying related to those fields.
class ServiceProviderAdmin(admin.ModelAdmin):
...
def get_form(self, request, obj=None, **kwargs):
self.fields = (
"name_registration",
"name_social",
"nome_usual",
("gender","document"),
"department",
"department_aditionals",
"picture",
"active",
)
if request.user.has_perm("rh.can_edit_secondary_mail"):
self.fields = self.fields + ("email_secondary",)
self.form = ServiceProviderFormFactory(request.user)
return super().get_form(request, obj=obj, **kwargs)
def ServiceProviderFormFactory(user):
class ServiceProviderForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
...
class Meta:
model = ServiceProvider
exclude = ("",)
I managed to resolve this. The ModelAdmin fields were being set in the get_form method, but it seems that on first access, Django fetches the list of fields before getting to the get_form method.
As the list of fields was not yet defined, it got all the fields related to the model.
Changing the fields definition from get_form to get_fields solved the problem:
class ServiceProviderAdmin(admin.ModelAdmin):
...
def get_fields(self, request, obj=None):
fields = (
"name_registration",
"name_social",
"nome_usual",
("gender","document"),
"department",
"department_aditionals",
"picture",
"active",
)
if request.user.has_perm("rh.can_edit_secondary_mail"):
fields = fields + ("email_secondary",)
return fields

Initial data for Django Admin inline formset is not saved

I'm trying to pre-fill some inlines in Django Admin with data passed as query params (in case of adding a new object in DB).
class TestCaseInlineFormSet(BaseInlineFormSet):
class Meta:
model = TestCase
fields = '__all__'
def __init__(self, *args, **kwargs):
super(TestCaseInlineFormSet, self).__init__(*args, **kwargs)
ids_string = self.request.GET.get('ids')
if ids_string:
ids = [int(x) for x in ids_string.split(',')]
self.initial = [{'test_case': id} for id in ids]
class TestCaseInline(admin.TabularInline):
model = TestCase
raw_id_fields = ('test_case',)
extra = 1
formset = TestCaseInlineFormSet
def get_formset(self, request, obj=None, **kwargs):
formset = super(TestCaseInline, self).get_formset(request, obj, **kwargs)
formset.request = request
return formset
def get_extra(self, request, obj=None, **kwargs):
extra = super(TestCaseInline, self).get_extra(request, obj, **kwargs)
requested_extras = len(request.GET.get('ids', '').split(','))
return max(extra, requested_extras)
The data is pre-filled fine with this solution, however there's an issue when trying to submit: the pre-filled inlines are not marked as changed, so they're not saved.
I've tried overriding has_changed() on the TestCaseInlineFormSet however it doesn't solve the problem - it seems has_changed() for the formset is never called?
Any idea how to fix this?

Django admin dynamic form fields population

I have Game model with ManyToMany relation on Taxonomy model witch has multiple types (PLATFORM, GENRE, FEATURE etc.)
class Game(models.Model):
taxonomy = models.ManyToManyField(Taxonomy)
class Taxonomy(models.Model):
TAXONOMY_ORDER = [
'PLATFORM',
'GAME_PROCESS',
'GRAPHICS',
'GENRE',
'CATEGORY',
'FEATURE'
]
type = models.CharField(choices=TAXONOMY_TYPES.items(), max_length=15)
I want to remove taxonomy field from admin and add separate MultiplueChoises field for each taxonomy type from TAXONOMY_ORDER
class GameAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(GameAdminForm, self).__init__(*args, **kwargs)
taxonomy_active = kwargs['instance'].taxonomy.all().values_list('id', flat=True)
for tax_type in Taxonomy.
self.fields['taxonomy_' + tax_type] = forms.MultipleChoiceField()
self.fields['taxonomy_' + tax_type].queryset = Taxonomy.objects.filter(type=tax_type)
self.Meta.fields.append('taxonomy_' + tax_type)
self.initial['taxonomy' + tax_type] = Taxonomy.objects.filter(
id__in=taxonomy_active,
type=tax_type
).values_list('id', flat=True)
class GameAdmin(admin.ModelAdmin):
form = GameAdminForm
def get_fieldsets(self, request, obj=None):
fieldsets = super(GameAdmin, self).get_fieldsets(request, obj)
for tax_type in Taxonomy.TAXONOMY_ORDER:
fieldsets[0][1]['fields'] += ['taxonomy_' + tax_type]
return fieldsets
I have two issues with this:
When I try add fields dynamicly I recieve an error
Unknown field(s) (taxonomy_FEATURE, taxonomy_PLATFORM, taxonomy_CATEGORY, taxonomy_GRAPHICS, taxonomy_GENRE, taxonomy_GAME_PROCESS) specified for Game. Check fields/fieldsets/exclude attributes of class GameAdmin.
When I try to add custom fields explicitly they are rendered blank
class GameAdminForm(forms.ModelForm):
taxonomy_PLATFORM = forms.MultipleChoiceField()
taxonomy_GAME_PROCESS = forms.MultipleChoiceField()
taxonomy_GRAPHICS = forms.ChoiceField()
taxonomy_GENRE = forms.MultipleChoiceField()
taxonomy_CATEGORY = forms.MultipleChoiceField()
taxonomy_FEATURE = forms.MultipleChoiceField()
def __init__(self, *args, **kwargs):
***__init__ stuff***
I don't have the rep for a comment, so this'll have to be an answer. I have been trying to solve the same problem for my own project, and the best solution I have found is this self-answered question by chadgh: How do I create and save dynamic fields in Django ModelAdmin?.
I have tried this method in my code. It works perfectly, and I think it does exactly what you're trying to accomplish. The only caveat is that in
def get_form(self, request, obj=None, **kwargs):
kwargs['fields'] = flatten_fieldsets(self.declared_fieldsets)
return super(PersonAdmin, self).get_form(request, obj, **kwargs)
self.declared_fieldsets is deprecated as of Django 1.7. I used self.fieldsets instead and it worked fine.
The main difficulty is that ModelAdmin normally gets the fields from the form class before it's instantiated, before the form's __init__ has executed, so it doesn't see the dynamic fields. That's why you have to override ModelAdmin.get_form.
I find out hot to solve dynamic MultipleChoiceField population with data, but still looking for proper solution of adding custom dynamic fields to ModelForm.
To populate MultipleChoiceField with existing values we need to pass choises on initialization:
forms.MultipleChoiceField(choices=choises)
To do that in dynamic way, we need to add our field to self.fields in __init__
self.fields['dynamic_field_name'] = forms.MultipleChoiceField(choices=choises)
To pass selected values:
self.initial['dynamic_field_name'] = initial_value
Complete code:
class GameAdminForm(forms.ModelForm):
dynamic_field = forms.MultipleChoiceField()
def __init__(self, *args, **kwargs):
super(GameAdminForm, self).__init__(*args, **
choises = Taxonomy.objects.all().values_list('id', 'name')
self.fields['dynamic_field'] = forms.MultipleChoiceField(choices=choises)
self.initial['dynamic_field'] = active_id_list
class Meta:
model = Game
fields = '__all__'
class GameAdmin(admin.ModelAdmin):
form = GameAdminForm
exclude = ['rating', '_id']
admin.site.register(Game, GameAdmin)

Update in three models at the same time in form_valid django

I have three models, Propietario, Administrador and Encargado. And I have three forms with ModelForm calling each of these models. The forms may to have three options depends of the user does:
If user picked option A, will display three forms where can fill different data for each form
If user picked option B, will display two forms, where the data filled in the form FormPropietario will save in Propietario model and automatically in Administrator model the same data. And the second form would be the form of Encargado model.
If user picked option C, will display only one form where the data filled here will save in all three models.
Note: Data filled in any of model will replace the other one, not has to create a new one. I mean, If user at the beginning picked option A and filled three different data, and then choose option C, the data filled in option C has to replace data in the other models, not has to create a new one.
To achieve this, i have been trying with this code:
Forms:
class FormPropietario(ModelForm):
def __init__(self, *args, **kwargs):
super(FormPropietario, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'input-medium'
self.fields['primer_nombre'].widget.attrs['required'] = True
class Meta():
model = Propietario
exclude = ("predio",'rol',)
widgets = {
'fecha_nacimiento' : forms.DateInput(attrs={'type':'date'}),
}
class FormAdministrador(ModelForm):
def __init__(self, *args, **kwargs):
super(FormAdministrador, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'input-medium'
self.fields['primer_nombre'].widget.attrs['required'] = True
class Meta():
model = Administrador
exclude = ("predio",'rol')
class FormEncargado(ModelForm):
def __init__(self, *args, **kwargs):
super(FormEncargado, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'input-medium'
self.fields['primer_nombre'].widget.attrs['required'] = True
class Meta():
model = Encargado
exclude = ("predio",'rol',)
The view that handles the option A, works fine:
#PAE
class PAEPropietarioView(UpdateModelMixin,UpdateView):
model = Propietario
form_class = FormPropietario
success_url = '/'
template_name = 'productores/PAE/propietario.html'
def form_valid(self,form):
propietario = Propietario()
propietario = form.save(commit=False)
propietario.rol.add(1)
return super(PAEPropietarioView,self).form_valid(form)
But problem is the view that handles the option B or C. I have this right now but with no success:
class PropietarioAndAdministratorView(UpdateModelMixin,UpdateView):
model = Propietario
form_class = FormPropietario
success_url = '/'
template_name = 'productores/PE/propietario.html'
def form_valid(self, form):
is_valid = super(PropietarioAndAdministratorView,self).form_valid(form)
if is_valid:
admin = Administrador.objects.get_or_create(predio_id=self.kwargs['predio_id'],**form.cleaned_data)
return True
return False
I also tried with this line, but nothing happens:
def form_valid(self, form):
Administrador.objects.get_or_create(predio_id=1,**form.cleaned_data)
return super(PropietarioAndAdministratorView, self).form_valid(form)
I think that I'm close, but the following line has an error; effectively saves in Administrator but save it twice, one with the data and other one empty. Why?
Administrador.objects.get_or_create(predio_id=self.kwargs['predio_id'],**form.cleaned_data)
I have been working on that for days and I cant get to put it works, how can I achieve it? Thanks in advance.

CBV Django Form View set data for ChoiceField

I'm using the Django Form View and I want to enter custom choices per user to my Choicefield.
How can I do this?
Can I use maybe the get_initial function?
Can I overwrite the field?
When I want to change certain things about a form such as the label text, adding required fields or filtering a list of choices etc. I follow a pattern where I use a ModelForm and add a few utility methods to it which contain my overriding code (this helps keep __init__ tidy). These methods are then called from __init__ to override the defaults.
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('country', 'contact_phone', )
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
self.set_querysets()
self.set_labels()
self.set_required_values()
self.set_initial_values()
def set_querysets(self):
"""Filter ChoiceFields here."""
# only show active countries in the ‘country’ choices list
self.fields["country"].queryset = Country.objects.filter(active=True)
def set_labels(self):
"""Override field labels here."""
pass
def set_required_values(self):
"""Make specific fields mandatory here."""
pass
def set_initial_values(self):
"""Set initial field values here."""
pass
If the ChoiceField is the only thing you're going to be customising, this is all you need:
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('country', 'contact_phone', )
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
# only show active countries in the ‘country’ choices list
self.fields["country"].queryset = Country.objects.filter(active=True)
You can then make your FormView use this form with like this:
class ProfileFormView(FormView):
template_name = "profile.html"
form_class = ProfileForm