4👍
I realized that I solved the problem myself and hadn’t answered here.
What I finally did is to override the Environment class save_model method instead for using the admin forms.
I’ll explain a little bit better:
I have an environment object and a server object. An environment has a number of servers that are linked to it via a foreign key into the server object. My goal was to populate the servers associated to an environment in the environment creation process. To be able to do that what I did was override the save_model method for the Environment object, do an obj.save() and AFTERWARDS create the Server objects that point to this environment, and then obj.save() again. Why afterwards? Because I can’t relation a new created server with an environment that doesn’t exist yet. Let me know if there is someone interested on he actual code.
14👍
Here is my implementation, thanks to Steven for the idea.
All in admin.py:
class SecondaryModelInline(admin.ModelAdmin):
model = SecondaryModel
formset = SecondaryModelInlineFormSet
def get_formset(self, request, obj=None, **kwargs):
formset = super(SecondaryModelInline, self).get_formset(request, obj, **kwargs)
formset.request = request
return formset
def get_extra(self, request, obj=None, **kwargs):
extra = super(SecondaryModelInline, self).get_extra(request, obj, **kwargs)
something = request.GET.get('something', None)
if something:
extra = ... figure out how much initial forms there are, from the request ...
return extra
Someplace before, also in admin.py, this:
class SecondaryModelInlineFormSet(forms.models.BaseInlineFormSet):
model = SecondaryModel
def __init__(self, *args, **kwargs):
super(SecondaryModelInlineFormSet, self).__init__(*args, **kwargs)
if self.request.GET.get('something', None):
# build your list using self.request
self.initial=[{'field_a': 'A', ...}, {}... ]
- [Django]-A Better Django Admin ManyToMany Field Widget
- [Django]-Django form with BooleanField always invalid unless checked
- [Django]-How to specify the login_required redirect url in django?
8👍
Not sure exactly why you want to do this, but perhaps you could create a modelformset:
from django.forms.models import BaseModelFormSet
class ServerFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(ServerFormSet, self).__init__(*args, **kwargs)
self.initial = [{ 'name': 's1', }, {'name': 's2'},] # supply your list here
and set this on your inline:
class ServerInline(admin.TabularInline):
form = ServerInlineAdminForm
model = Server
extra = 39
formset = ServerFormSet
I have not tried this out.
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#providing-initial-values
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-initial-data-with-a-formset
- [Django]-How do I use Django's MultiWidget?
- [Django]-Passing variable urlname to url tag in django template
- [Django]-Custom error message in Django admin actions
4👍
The ModelAdmin
has a method get_formset_kwargs
that can be used for exactly this scenario with very few lines of code:
class ServerInline(admin.TabularInline):
model = Server
class EnvironmentAdmin(admin.ModelAdmin):
inlines = [ServerInline]
def get_formset_kwargs(self, request, obj, inline, prefix):
formset_params = super().get_formset_kwargs(request, obj, inline, prefix)
if not obj.id and isinstance(inline, ServerInline):
formset_params |= {"initial": [
{"name": f"Testing {i}"} for i in range(39)
]}
return formset_params
Please note: the dict merge operator |
is only available from Python 3.9 onwards. Use dict.update()
on older versions.
- [Django]-Django: ImproperlyConfigured: The SECRET_KEY setting must not be empty
- [Django]-Select distinct values from a table field
- [Django]-Can Django automatically create a related one-to-one model?
3👍
I haven’t tried this but since the extra forms generated are essential Django Formsets, what you need to do is bind initial data to the formset which is explained in the docs here.
I just read through the docs and it looks like you can define your own formset inside your inlineadmin and then as mentioned above, prepopulate the formset with data from your list. I think you could achieve that by placing the prepopulation code in your class’ init method.
I know this isn’t a very elaborate explanation but I found the question interesting and looked up the docs and thought maybe I could point you in the right direction with what to try next.
- [Django]-Should I avoid multi-table (concrete) inheritance in Django by any means?
- [Django]-Django – Cannot create migrations for ImageField with dynamic upload_to value
- [Django]-Django Rest Framework ImageField
2👍
Well, I wanted to comment on frnhr’s answer, but did not have enough reputation, so:
The answer worked for me, I just needed to loop through the forms in the formset and set the initial data for each of them:
class SecondaryModelInlineFormSet(forms.models.BaseInlineFormSet):
model = SecondaryModel
def __init__(self, *args, **kwargs):
super(SecondaryModelInlineFormSet, self).__init__(*args, **kwargs)
if self.request.GET.get('something', None):
# build your list using self.request
for form in self:
form.initial = {'field_a':'A',...} #This is what I changed
self.initial=[{'field_a': 'A', ...}, {}... ]
- [Django]-Django equivalent of PHP's form value array/associative array
- [Django]-Django vs other Python web frameworks?
- [Django]-Allowing RabbitMQ-Server Connections
1👍
It worked for me in case of prepopulating user
from request.user
for StackedInline
and TabularInline
.
def save_formset(self, request, form, formset, change):
for form in formset.forms:
form.instance.user = request.user
formset.save()
- [Django]-Get date from a Django DateTimeField
- [Django]-How to add custom view to django admin interface?
- [Django]-Django Admin filter on Foreign Key property
0👍
Adding the initial
elements in the inline class as all the other answers suggest won’t be enough to actually get the elements saved.
Django ignores the initial elements that were not modified by users, so you need to add some extra logic to your InlineFormSet
to save the initial data:
from django.contrib import admin
from django.forms import BaseInlineFormSet
class InitialElementsInlineFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.initial_extra is not None:
self.extra = len(self.initial_extra)
def save_new_objects(self, commit=True):
super().save_new_objects(commit)
for form in self.extra_forms:
if not form.has_changed() and form.initial in self.initial_extra:
instance = form.instance
instance.__dict__.update(form.initial)
setattr(instance, self.fk.name, self.instance)
self.new_objects.append(instance)
if commit:
instance.save()
return self.new_objects
This Formset class is doing two things:
- In the
__init__
method, we’re setting the initial form number to be the same as your initial elements, so all of them are displayed. - When the form is submitted, it saves the elements that weren’t modified. Be careful with this: if you didn’t supply all the required fields in
initial
, it will fail to create the object, resulting in a Server Error response.
Once you have created this class, you should be able to use it in your admin models like this:
class MyModelInline(admin.TabularInline):
model = MyModel
formset = InitialElementsInlineFormSet
def get_initial(self, request):
data = [
{
"field_a": "example",
"user_id": request.user.id,
"favorite_number": i,
}
for i in range(3)
]
return data
class ParentAdmin(admin.ModelAdmin):
inlines = [MyModelInline]
def get_formset_kwargs(self, request, obj, inline, prefix):
formset_params = super().get_formset_kwargs(request, obj, inline, prefix)
if isinstance(inline, MyModelInline) and obj.pk is None:
formset_params.update(initial=inline.get_initial(request))
return formset_params
Override the Inline class’ get_initial
method to populate the forms you want to see when creating a new object. You can access the user object as well as data from the parent using the request
object.
In the ParentAdmin
class, we’re making sure we only populate the initial keyword for our Inline
class. By checking if obj.pk is None
, we ensure this happens only during the creation of a Parent
object. You shouldn’t need to modify this part.
- [Django]-Django dumpdata UTF-8 (Unicode)
- [Django]-Django image resizing and convert before upload
- [Django]-Django check to see if field is blank?