Customize admin import forms - django

I want to add an additional field to my Import-Form on the Admin page.
I did everything the Docs say but the entered values for year and calender_week wont show up.
resources.py
class ForecastListResource(resources.ModelResource):
year = fields.Field(column_name="Jahr", attribute="year")
calender_week = fields.Field(column_name="Kalenderwoche", attribute="calender_week")
brand = fields.Field(column_name="Marke", attribute="brand")
material = fields.Field(column_name="Material", attribute="material")
material_short_text = fields.Field(column_name="Materialkurztext", attribute="material_short_text")
gmc = fields.Field(column_name="GMC", attribute="gmc")
gmc_text = fields.Field(column_name="GMC Text", attribute="gmc_text")
bed_date = fields.Field(column_name="BedTermin", attribute="bed_date")
bed_amount = fields.Field(column_name="BedMenge", attribute="bed_amount")
bed_week = fields.Field(column_name="BedWoche", attribute="bed_week")
code_last_bed_week = fields.Field(column_name="Code letzte KW", attribute="code_last_bed_week")
fabric_number = fields.Field(column_name="Stoffnummer", attribute="fabric_number")
print_stage_3 = fields.Field(column_name="Druckstufe 3", attribute="print_stage_3")
average_filling = fields.Field(column_name="Mittelwert Abfüllung", attribute="average_filling")
net = fields.Field(column_name="Netto", attribute="net")
class Meta:
model = ForeCastList
use_bulk = True
skip_unchanged = True
forms.py
class ForecastDoDImportFormMixin(forms.Form):
calender_week = forms.IntegerField(label="Kalenderwoche", required=True)
year = forms.IntegerField(label="Jahr", required=True)
class ForecastDoDImportForm(ForecastDoDImportFormMixin, ImportForm):
pass
class ForecastDoDConfirmImportForm(ForecastDoDImportFormMixin, ConfirmImportForm):
pass
admin.py
#admin.register(ForeCastList)
class ForeCastList(ImportExportModelAdmin):
resource_class = ForecastListResource
def get_import_form(self):
return ForecastDoDImportForm
def get_confirm_import_form(self):
return ForecastDoDConfirmImportForm
def get_form_kwargs(self, form, *args, **kwargs):
if isinstance(form, ForecastDoDImportForm):
if form.is_valid():
kwargs.update({"calender_week": form.cleaned_data["calender_week"], "year": form.cleaned_data["year"]})
return kwargs
def get_import_data_kwargs(self, request, *args, **kwargs):
print(kwargs)
return super().get_import_data_kwargs(request, *args, **kwargs)
Import-Form
Result
-> related Part from the Doc´s:
https://django-import-export.readthedocs.io/en/latest/getting_started.html#customize-admin-import-forms

If I understand correctly, you want to select a value from a dropdown and have that inserted for all rows. This means that if there is a value for 'Jahr' in the import file, this will be ignored in favour of the value selected from the dropdown.
Setting an import field to the selection in the dropdown can be achieved by assigning the value of the dropdown onto the instance of the object to be imported as follows. (I've used the single field 'year' instead of 'Jahr' and 'Kalenderwoche' but you can update your implementation along the same lines).
class ForecastListResource(resources.ModelResource):
year = fields.Field(column_name="Jahr", attribute="year")
# (other fields removed for brevity
def after_import_instance(self, instance, new, row_number=None, **kwargs):
# override to set the value of the dropdown onto the row instance
instance.year = kwargs.get("year")
class Meta:
model = ForeCastList
use_bulk = True
skip_unchanged = True
Declare the ModelAdmin class to read the 'year' value from the form:
#admin.register(ForeCastList)
class ForeCastList(ImportExportModelAdmin):
resource_class = ForecastListResource
# other methods removed
def get_import_data_kwargs(self, request, *args, **kwargs):
form = kwargs.get('form')
if form:
return {"year": form.cleaned_data["year"]}
return dict()
I was able to complete this in the example app:

Related

How to access run-time request.session values in forms.py definition?

I have an inventory management app that will be serving multiple locations (called contexts in my app). When a user is logged in, their current context is stored as a value in request.sessions.
I would like users to only be able to browse and retrieve records for their own location.
I've been trying to this by filtering the queryset that is called in the form definition to populate the select dropdown, i.e.
referenced_catalog = forms.ModelChoiceField(
queryset=Inventory_unit_catalog.objects.all().filter(parent_business_unit_context_id=user_context_id),
I've tried implementing several different (but similar) approaches from various SO posts, that involve defining an init block to the form, such as:
class InventoryStockAddForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user_context_id = kwargs.pop('user_context_id', None)
super(InventoryStockAddForm, self).__init__(*args, **kwargs)
name = forms.CharField(max_length=96,widget=forms.TextInput(),required=True)
referenced_catalog = forms.ModelChoiceField(
queryset = Inventory_unit_catalog.objects.all().filter(parent_business_unit_context_id=self.user_context_id),
label = u"",
widget = ModelSelect2Widget(
model=Inventory_unit_catalog,
search_fields=['name__icontains'],
attrs={'data-placeholder': 'Select catalog...', 'data-width': '35em'},
required=False))
class Meta():
model = Inventory_unit_stock
fields = ('name',)
(Different SO answers had one way or the other.)
Then in views.py:
user_context_id = request.session.get('user_context_id')
...
add_form = InventoryStockAddForm(user_context_id=user_context_id)
I've even tried using the SessionStore per https://djangobook.com/using-sessions-views-2/:
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
s = SessionStore()
user_context_id = s['user_context_id']
but it always fails at the moment the forms.py is updated as Django validates the code and cannot find a key value at the moment of validation.
Any advice would be appreciated, thanks!
You can't access self.user_context_id inside referenced_catalog = forms.ModelChoiceField(...) - that code runs when the module is loaded, not when the form is initialised.
Instead, you should set the queryset inside the __init__ method.
class InventoryStockAddForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user_context_id = kwargs.pop('user_context_id', None)
super(InventoryStockAddForm, self).__init__(*args, **kwargs)
self.fields['referenced_catalog'].queryset = Inventory_unit_catalog.objects.all().filter(parent_business_unit_context_id=self.user_context_id)
referenced_catalog = forms.ModelChoiceField(
queryset = Inventory_unit_catalog.objects.none(),
label = u"",
widget = ModelSelect2Widget(
model=Inventory_unit_catalog,
search_fields=['name__icontains'],
attrs={'data-placeholder': 'Select catalog...', 'data-width': '35em'},
required=False))

Django: send dictionary/list data to form

GOAL: Send a dictionary of data to a form, to be used in a dropdown boxself.
Views.py
form = FormsdbForm(initial={'user': default_state})
# (to set the default value of the 'user' field)
Forms.py
class FormsdbForm(forms.ModelForm):
ROOMLIST = (('roomID_abcdefghi','Room ABC'),
('roomID_jklmnopqr','Room JKL'))
roomid = forms.ChoiceField(required=False, choices=ROOMLIST)
class Meta:
model = Formsdb
fields = ('user', 'uniqueid', 'roomid')
The above setup displays a form where the field 'roomid' is a dropdown box showing to entries:
Room ABC
Room JKL
After saving, the database is populated with the matching 'RoomID_xxxxxxxxx'
Perfect so far!
In my Views.py I have a dictionary (that I can easily convert into a list-of-lists) with the data that is now statically configured in Forms.py (ROOMLIST).
QUESTION: How can I pass this dictionary (or list) to the form so it displays a dropdown box with choices?
This would replace the current "ROOMLIST" variable and it could easily contain 400-600 entries.
The view:
from django.views.generic import FormView
class FormsdbView(FormView):
# ...
def get_form_kwargs(self):
kwargs = super(FormsdbView, self).get_form_kwargs()
ROOMLIST = (('roomID_abcdefghi','Room ABC'),
('roomID_jklmnopqr','Room JKL'))
kwargs['roomlist'] = ROOMLIST
return kwargs
If you're not using FormView, you might also do form = FormsdbForm(initial={'user': default_state}, roomlist=ROOMLIST)
The form:
from django import forms
class FormsdbForm(forms.ModelForm):
roomid = forms.ChoiceField(required=False)
# ...
def __init__(self, *args, **kwargs):
self.roomlist = kwargs.pop('roomlist', None)
super(FormsdbForm, self).__init__(*args, **kwargs)
self.fields['roomid'].choices = self.roomlist

Django combine two fields data into a queryset

I have a model say Club where we have fields like:
manager = models.ForeignKey(Users, related_name="return_manager", on_delete=models.CASCADE)
members = models.ManyToManyField(Users, related_name="return_members", blank=True)
Now I want to create a drop down in a form where I can add both the manager and members to it. I tried making two requests for Club.objects.filter(pk=mypk).members.all() and Club.objects.filter(pk=mypk).manager. I tried chain function and using '| ' operator but none worked. I think the manager is a single User and not a queryset, that is what the main problem is. Any workarounds?
One possible way getting all of the information together involves modifying your form choices.
In your view you would need to pass the choices along as context to your form.
def my_view(request, club_pk):
context = {}
context.update({
"manager": Club.objects.get(pk=club_pk).manager,
"members": Club.objects.get(pk=club_pk).members.all()
}
form = MyForm(request.POST or None, request=request, context=context)
In your form, you would need to modify the __init__ method to update your choices like so:
class MyForm(forms.Form):
all_club_members = forms.ChoiceField('Manager + Members', required=True)
def __init__(self, *args, **kwargs):
self.context = kwargs.pop('context', None)
super(MyForm, self).__init__(*args, **kwargs)
manager_tuple = [(self.context['manager'].id, self.context['manager'].display_name)]
members_tuples = [(member.id, member.display_name) for member in self.context['members']
self.fields['all_club_members'].choices = manager_tuple + members_tuples
Try this:
manager = [Club.objects.filter(pk=mypk).manager]
members = Club.objects.filter(pk=mypk).members.all()
userlist = list(manager) + list(members)
return Users.objects.filter(pk__in=userlist)
Should create a queryset of all users

ModelChoiceField in forms.Form won't validate if queryset is overridden

I have a django ModelChoiceField that won't validate if I override the queryset.
class PersonalNote(forms.Form):
tile = ModelChoiceField(queryset=Tile.objects.none())
note = forms.CharField()
form = PersonalNote()
form.fields['tile'].queryset = Tile.objects.filter(section__xxx=yyy)
The form.is_valid() error is: "Select a valid choice. That choice is not one of the available choices".
If Tile.objects.none() is replaced with Tile.objects.all() it validates, but loads far too much data from the database. I've also tried:
class PersonalNote(forms.Form):
tile = ModelChoiceField(queryset=Tile.objects.none())
note = forms.CharField()
def __init__(self, *args, **kwargs):
yyy = kwargs.pop('yyy', None)
super(PersonalNote, self).__init__(*args, **kwargs)
if yyy:
self.fields['tile'].queryset = Tile.objects.filter(section__xxx=yyy)
What might be wrong here? Note the real application also overrides the label, but that does not seem to be a factor here:
class ModelChoiceField2(forms.ModelChoiceField):
def label_from_instance(self, obj):
assert isinstance(obj,Tile)
return obj.child_title()
After 2 hours I found the solution. Because you specified a queryset of none in the class definition, when you instantiate that PersonalNote(request.POST) to be validated it is referenceing a null query set
class PersonalNote(forms.Form):
tile = ModelChoiceField(queryset=Tile.objects.none())
note = forms.CharField()
To fix this, when you create your form based on a POST request be sure to overwrite your queryset AGAIN before you check is_valid()
def some_view_def(request):
form = PersonalNote(request.POST)
**form.fields['tile'].queryset = Tile.objects.filter(section__xxx=yyy)**
if form.is_valid():
#Do whatever it is
When you pass an empty queryset to ModelChoiceField you're saying that nothing will be valid for that field. Perhaps you could filter the queryset so there aren't too many options.
I also had this problem. The idea is to dynamically change the queryset of a ModelChoiceField based on a condition (in my case it was a filter made by another ModelChoiceField).
So, having the next model as example:
class FilterModel(models.Model):
name = models.CharField()
class FooModel(models.Model):
filter_field = models.ForeignKey(FilterModel)
name = models.CharField()
class MyModel(models.Model):
foo_field = models.ForeignKey(FooModel)
As you can see, MyModel has a foreign key with FooModel, but not with FilterModel. So, in order to filter the FooModel options, I added a new ModelChoiceField on my form:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
# your code here
self.fields['my_filter_field'] = forms.ModelChoiceField(FilterModel, initial=my_filter_field_selected)
self.fields['my_filter_field'].queryset = FilterModel.objects.all()
Then, on your Front-End you can use Ajax to load the options of foo_field, based on the selected value of my_filter_field. At this point everyting should be working. But, when the form is loaded, it will bring all the posible options from FooModel. To avoid this, you need to dynamically change the queryset of foo_field.
On my form view, I passed a new argument to MyForm:
id_filter_field = request.POST.get('my_filter_field', None)
form = MyForm(data=request.POST, id_filter_field=id_filter_field)
Now, you can use that argument on MyForm to change the queryset:
class MyForm(forms.ModelForm):
# your code here
def __init__(self, *args, **kwargs):
self.id_filter_field = kwargs.pop('id_filter_field', None)
# your code here
if self.id_filter_field:
self.fields['foo_field'].queryset = FooModel.objects.filter(filter_field_id=self.id_filter_field)
else:
self.fields['foo_field'].queryset = FooModel.objects.none()

Django multipleselect selected data

I've been trying to crack this nut for a while but I cannot seem to be able to set selected values for multipleselect. What I have is:
class GroupUsersForm(forms.Form):
users = forms.MultipleChoiceField()
def __init__(self, *args, **kwargs):
super(GroupUsersForm, self).__init__(*args, **kwargs)
if kwargs.has_key('initial'):
self.selected = kwargs['initial']['users']
self.choices = kwargs['initial']['all']
self.fields[self.get_field_name()] = self.get_users_field()
def get_users_field(self):
field_class = forms.MultipleChoiceField
field = field_class(
label=_(u"users"),
choices = self.get_field_choices(),
widget = FilteredSelectMultiple(_(u"users"), is_stacked= False),
#initial = self.get_field_initial(),
required= False
)
return field
def get_field_name(self):
return 'users'
def get_field_choices(self):
choices = [(p.id, p.username) for p in self.choices]
return choices
def get_field_initial(self):
selected = [(p.id, p.username) for p in self.selected]
return selected
and i initiate it like that:
uform = GroupUsersForm(initial = {'all': users, 'users':u, 'group':g,})
both users and u are querysets (
users = get_users_with_perms(a).order_by('username', 'last_name', 'first_name')
u = User.objects.filter(Q(groups = g)).order_by('username', 'last_name', 'first_name'))
which in one test case were even the same.
No selected options showed up.
I tried setting the initial values myself manually to list based of object ids, passing single id, passing (id, username) tuple and so on... tried using different widget, but all widgets display all the options but none of them are selected.
what am i doing wrong?
Alan
Try setting the choices property of the users field. The value of 'users' in the initial dict should be a list of whatever the keys are for the user choices. For example, if
self.fields['users'].choices = (
('username1','Firstname Lastname'),
('username2','Firstname Lastname'),
('username3','Firstname Lastname'),
)
Initial should be ['username1', 'username3']
Then change your form instantiation and __init__ to the following:
uform = GroupUsersForm(users, initial = {'users':u,})
def __init__(self, user_choices, *args, **kwargs):
super(GroupUsersForm, self).__init__(*args, **kwargs)
self.fields['users'].choices = user_choices