Add field to Django queryset - django

I'm working on a small project, with an existing database, filled with data. Every day we run a task to get a number of records and email the results. For one of the values we email, we need to perform a calculation (string replacement) on an existing field. We now do this using a model method. But to do this, we need to iterate over all the objects in the queryset.
How can I add/extend a calculated field to the queryset at the moment that we run the query? Note that this is not an existing field in the model, and that we have to perform some replacements on an existing field. Can I use a custom model manager for this? And if so, do you have an example?
The model method:
def get_application_dir(self):
trails = {
'xxx': '/abc/123',
'yyy': '/xyz/789',
}
app = self.application
file = self.filename
if app in trails:
traillen = len(trails[app])
if file[-traillen:] == trails[app]:
return file[:-traillen]
return file
The use in the view:
if files:
result['i'] = []
for f in files:
a = {
'application_name': f.application_name,
'directory': f.get_application_dir()
}
result['i'].append(a)
So, I don't want to perform the loop above, by just getting the value/field from the ORM. Is this possible? Maybe by creating a custom model manager, and extending the queryset?
Thanks in advance.

Related

How to replace foreign key by its associated value in django queryset

I'm fairly new to django and i would need your help!
I wrote an api/route view that query the database and return a JSON to my fetch function in my javascript.
Is there a way to query the database and got back a queryset with foreign key replaced by its associated value ?
I though i could use ModelName.objects.select_related( 'field ') but i didn't understand how it works.
If you have a solution or advices on better way to achieve the goal, Thanks in advance!
Context in pseudo-code:
// HTML //
Button onclick function get_list_of_data
// JS //
function get_list_of_data:
Fetch URL of route django
Convert response to JSON
iterating through JSON to fill HTLM div
// Django //
use ModelName.objects.filter( name = name ) to get list of data in Queryset
use serializers.serialize(json, ...") to get JSON from Queryset
return JsonResponse (json_query)
If I understood the problem well, when you serialize a model that has a ForeignKey field defined you get only the id value in JSON response but you would like to get the whole object (not only a number) returned.
The way to do that is to specifically write serializer for that ForeignKey model and then use it within the serializer od the model that you are trying to fetch.
You haven't provided any code, but here is some example that might help you:
class SecondModelSerializer(serializers.ModelSerializer):
class Meta:
model = SecondModel
fields = '__all__'
class FirstModelSerializer(serializers.ModelSerializer):
foreign_key_field = SecondModelSerializer()
class Meta:
model = FirstModel
fields = ('id', 'foreign_key_field', 'field1', 'field2')
Here in your FirstModelSerializer you specifically told Django to use SecondModelSerializer for your ForeignKey field (I named it foreign_key_field). This way Django will know how to serialize that field instead of returning only the id value.

Flask-admin editable select column row-based filter

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

How to test CheckboxSelectMultiple in save

I have a form (ModelForm) in Django, where I am adding a field for users in the init method as so:
self.fields["users"] = forms.ModelMultipleChoiceField(
queryset=users, widget=forms.CheckboxSelectMultiple, required=False,label="Add Designer(s)"
)
In the save method how I can iterate over the queryset for this field, however, I do not know how I can test if the particular model has been selected/checked. Help, please.
EDIT:
Let's say that you have a form where you want to be able to add users to a certain project, I set the users field as above (also usedMultipleChoiceField) but my real question is how do you determine the state of those checkboxes (which users should be added)?
Managed to fix it using MultipleChoiceField instead of ModelMultipleChoiceField. Then populated the choices with existing event IDs and passed it to the template.
In forms:
choices = forms.MultipleChoiceField(widget = forms.CheckboxSelectMultiple())
In views:
form.fields['choices'].choices = [(x.eventID, "Event ID: " + x.eventID) for x in unapproved]
Had to change some of the logic for finding and editing Event objects too.
The Django documentation states that a ModelMultipleChoiceField normalizes to a QuerySet of model instances. That means in your example, it will only return the users that have been checked. If none have been checked, it will return an empty QuerySet.
If you are overriding your ModelForm save method, you could include something like this:
selected_users = self.cleaned_data.get('users')
for user in selected_users:
project_users.add(user)

Django - force pk_url_kwarg to query other model instances

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.

Django's Inline Forms

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