I am trying to create records using an inline formSet while at the same time creating a record with a normal form whose primary key is the foreign key for the inline formSet all on the same HTML page.
Make sense? Here's what I mean: Suppose I have the following two models (not real code, obviously, but you get the idea):
Class mainModel
Primary Key (custom pk I create)
field1
field2
Class inlineFormModel
autoPK
field1 = ForeignKey(mainModel)
field2
Now, I want to create a single HTML page for the user so they can create a mainModel instance at the same time as creating a number of inlineFormModel instances. The mainModel would be a normal form while the inlineFormModel would be using inlineFormsets. The problem is that when I save all the forms, there is no foreignKey to link to the inline formSet records since the model it refers to is still be created (everything gets saved in the same view). Does that make sense?
How would I go about creating a new mainModel instance with several secondModel instances and save the whole batch all with the same view function?
Thanks!
This is a common scenario, I do not know why is not addressed in the docs:
initial_form = mainModelForm(request.POST)
if initial_form.is_valid():
form= initial_form.save(commit=False)
my_formset = inline_formset(request.POST,instance=form)
if my_formset.is_valid():
form.save()
my_formset.save()
....... .........
# return codes here
Related
I am using flask-admin to have easy edits on my DB model. It is about renting out ski to customers.
This is my rental view (the sql_alchemy DB model is accordingly):
class RentalView(ModelView):
column_list = ("customer", "from_date", "to_date", "ski", "remarks")
...
customer and ski are relationship fields to the respective model. I want to only show these ski in the edit view that are not rented by others in this time period.
I have been searching everywhere how to dynamically set the choices of the edit form but it simply does not work fully.
I tried doing
def on_form_prefill(self, form, id):
query = db.session.query... # do the query based on the rental "id"
form.ski.query = query
and this correctly shows the filtered queries. However, when submitting the form, the .query attribute of the QuerySelectField ski is None again, hence leading to a query = self.query or self.query_factory() TypeError: 'NoneType' object is not callable error. No idea why the query is being reset?!
Does anybody know a different strategy of how to handle dynamic queries, based on the edited object's id?
Use this pattern, override the view's edit_form method, instance the default edit form (by calling the super() method then modify the form as you wish:
class RentalView(ModelView):
# setup edit forms so that Ski relationship is filtered
def edit_form(self, obj):
# obj is the rental instance being edited
_edit_form = super(RentalView, self).edit_form(obj)
# setup your dynamic query here based on obj.id
# for example
_edit_form.ski.query_factory = lambda: Ski.query.filter(Ski.rental_id == obj.id).all()
return _edit_form
I use Django 2
This is what my view.py contains
class SchoolCreateView(CreateView):
fields = ("name","principal","location")
model = models.School
The template (html file) used by this view contains the code:
form.instance.pk
And it works. It returns the correct primary key. I don't understand why.
Why does this work when I have not defined the form object in my view? Is the value of form automatically assigned when using CBVs in Django?
Follow up question. I know that form.instance represents a row in the model but what does form itself represent? My current understanding with forms is that it represents request.POST from views.py (basing my knowledge from function views). But that wouldn't make sense because the client has yet to make a POST request since he is still going to create a data entry which will be posted but is not being posted yet.
Pk is a primary key field, which is id by default. if you define other field as primary key, calling pk will return this.
From documentation:
By default, Django gives each model the following field:
id = models.AutoField(primary_key=True)
This is an auto-incrementing primary key.
If you’d like to specify a custom primary key, specify primary_key=True on one of your fields. If Django sees you’ve explicitly set Field.primary_key, it won’t add the automatic id column.
Each model requires exactly one field to have primary_key=True (either explicitly declared or automatically added).
Consider the following code:
views.py
class BHA_UpdateView(UpdateView):
model = BHA_overall
pk_url_kwarg = 'pk_alt'
form_class = BHA_overall_Form
To my understanding, pk_url_kwarg = 'pk_alt' will query and return instances of model = BHA_overall.
Is there any way that I can force pk_url_kwarg to query
& return other model instances defined in models.py (like model = other_model), while having my get_object() method to return objects in model = BHA_overall? What CBV should I use (I think UpdateView is not a good choice in this case)?
++ I'm trying to make a page that allows users to manage information about the product they use. So, ultimately I will implement forms, and the user input needs to be saved in DB
++ I need pk_url_kwarg = 'pk_alt' to query other models and generate url. But I still need get_object() method to return objects in model = BHA_overall to generate form fields on the user side.
From my understanding you need a django form generated from BHA_overall, but the data should be saved to AnotherModel right?
I will propose 2 solutions to this problem, Choose what best fits you.
Multiple views:
Have multiple views for the task, What I mean is create a view which creates the form for the frontend using BHA_overall, you can create both Create and Update view this way and update view's initial could be overwritten so form will have expected value when editing. And now post the data to another view which handles the post data. This view can have your AnotherModel doing its thing.
Using Django Form:
If you dont like having multiple views, You can keep things simple by creating a form yourself. Create a DjangoForm with the same fields you want to show to the user and use it in to create your own views, Now you wont need BHA_overall and use your AnotherModel to save datal.
In django admin I have Model A with a foreign key association to Model B. Model B's values change based on the value of Model A.
When a Model B object is selected for association with a Model A object, I would like to immediately display updated values for Model B based on the current value of Model A.
I know that I can override the on_save method in the form to update the values when the user saves the form to the database. However, I would like the admin view to display the values before the user hits save.
What do I need to hook into to make this update happen?
Thank You
If you want to dinamically filter Model B values in the changeview during user interaction (that is: before submission), you need javascript:
1) after page rendering, attach a "change handler" to Model A input field
2) in that handler, call via Ajax a view to retrieve the list of values available for Model B according to the currently selected value of Model A
3) upon receiving the list, update the Model B input field accordingly
4) also, after the initial page rendering you should explicitly invoke the handler in order to have Model B input field correctly initialized
This should work for both "add" and "change" view.
I do believe that a very detailed tutorial on how to implement this procedure can be found here:
https://simpleisbetterthancomplex.com/tutorial/2018/01/29/how-to-implement-dependent-or-chained-dropdown-list-with-django.html
The example refers to a front-end view, but can be easily adapted to the admin changeview
Let's say here what you've got for models:
# Model B
class ModelB(models.Model):
pass
# Model A
class ModelA(models.Model):
b_link = models.ForeignKey(ModelB)
I assume that you don't want to use javascript to manipulate the form but parsing it from server. In that case, what you can do is just create a preview Model B, and create the ModelForm from this model.
For example:
class ModelB(models.Model):
...
# add a method to preview B - This will not save model
def preview_b(model_a):
# update values of b according to model_a
b.derived_value = model_a.value
# file: forms.py
class ModelBForm(ModelForm):
class Meta:
model = ModelB
# file: views.py
b_model = Model.objects.all().first()
b_model.preview_b(a_model)
form = ModelBForm(instance=b_model)
This, of course, requires you to post back to server whenever choosing a new ModelA but I think that was what you wanted.
I am trying to write my first unit test in Django. It's for a Staff registration form.
The Staff model for the form has a OneToOne relation with a UserProfile (AUTH_PROFILE_MODULE).
The UserProfile has a OneToOne relation with django.contrib.auth.models.User.
I am using https://github.com/dnerdy/factory_boy to create a test model instance for the staff model. The idea is to use a StaffFactory so I can easily create test model instances. To create a bound form I need to pass it a data dict. I thought it would be convenient to just use django.forms.models.model_to_dict to convert my model instance into a data dict when testing the form.
Now, my problem is: model_to_dict does not traverse the foreign keys of my Staff model (Staff->UserProfile->User). This means the form stays invalid since required fields like the User's email are still missing inside the form data.
Currently my StaffRegistrationFormTest looks like:
class StaffRegistrationFormTest(unittest.TestCase):
def test_success(self):
staff1 = StaffFactory()
form = StaffRegistrationForm(model_to_dict(staff1))
# print jsonpickle.encode(model_to_dict(staff1))
self.assertTrue(form.is_valid(), form.errors)
Is there a way to pass in a dict, where the foreign keys are serialized by re-using a model instance?
So it seems as if one way of solving this is by creating additional dictionaries for the OneToOne fields of the Staff model and merging them.
This makes the test pass:
data = dict(model_to_dict(staff1).items() +
model_to_dict(staff1.profile).items() +
model_to_dict(staff1.profile.user).items())
form = StaffRegistrationForm(data=data)
self.assertTrue(form.is_valid(), form.errors)
I am not sure if this is the way to go in terms of best practice. Feel free to comment if this it completely against the grain.