Django form - the same field multiple times - django

how can I process a form with a field:
order = ModelChoiceField(
required=False,
queryset=OrderOd.objects.filter(Q(status='DN') | Q(status='DI')),
widget=Select(
attrs={
"class": "form-select form-select-md form-select-solid",
"data-control": "select2",
"data-multiple": "true",
"multiple": "multiple",
"data-placeholder": _("Vyberte objednávku ..."),
"id": 'order'
}
)
)
In front-end, I can select multiple orders (looks like pills/tags) and in the request sent to the server it looks like this:
movement: f2b7c234-fbdb-4059-bcb6-8ada46cef72c
account: dbabefb7-f053-4edf-a2e3-787bf6bfc371
date: 2022-09-12
order: eb2fc726-3e97-4af2-a8b2-08f20771cfef
order: 8398925b-fca6-4b25-8c48-e12940a5b5c3
order: bfa35391-5cf8-4ed8-8c44-a797da875cb4
order: 07be93ac-20b3-459c-8038-c8b023db6d66
When I inspect self.data, I got
'order': ['eb2fc726-3e97-4af2-a8b2-08f20771cfef', '8398925b-fca6-4b25-8c48-e12940a5b5c3', 'bfa35391-5cf8-4ed8-8c44-a797da875cb4', '07be93ac-20b3-459c-8038-c8b023db6d66'],
but when I check the output of logger.info(self.data['order']), it gives me only the first UUID.
[INFO] form.py 123: 07be93ac-20b3-459c-8038-c8b023db6d66
What I need is to access all UUIDs in the array (order) and process them instance by instance.
Any idea, how to do it?
Thanks

You can use self.data.getlist('order') to return the data in the array form.
see more info in Django documentation

Related

Django Rest update many to many field of multiple objects at once

I'm working on chat app django rest backend. Btw I have a problem to update m2m field of multiple objects at once.
Inside the Message model there is an m2m field deleted which represents a list of users who deleted this message.
class Message(models.Model):
# other fields
deleted = models.ManyToManyField(User)
So I can implement the delete functionality by adding user inside that field when user deletes a specific message.
But the problem is when user deletes a conversation(all messages in it), how can I implement to update the delete field of multiple Message objects at once. Because each object has empty delete field, another user inside delete field, or same user inside delete field(means that user already deleted a message before).
Edit
According to validname's comment I'm adding an example to make clear about my problem.
Please imagine user1 and user2 has conversation1 and there are 3 messages inside that conversation. So currently message list of that conversation is like following:
[
{
id: 304,
conversation: 1,
...
deleted: []
},
{
id: 305,
conversation: 1,
...
deleted: [2] // user2 already deleted this message before
},
{
id: 306,
conversation: 1,
...
deleted: [1] // user1 already deleted this message before
},
]
And user2 just has deleted conversation1, so after that message list should be like following:
[
{
id: 304,
conversation: 1,
...
deleted: [2]
},
{
id: 305,
conversation: 1,
...
deleted: [2] // no need to change for existing user2
},
{
id: 306,
conversation: 1,
...
deleted: [1, 2] // insert user2
},
]
I just want a django function that works like above changes.
All m2m relations are implemented by extra table and have their own models which are accessible by field's thorough attribute.
So if you have a set of messages and you want to add user to 'deleted' for each of message, you can do it by bulk creating records for though model:
from itertools import cycle
message_qs = ... # Query set of message.
user = ... # User model instance.
# Construct list of [(message_1, user), (message_2, user), ...] etc.
data = list(zip(message_qs, cycle(user)))
# Transform data to through model instances to pass to bulk create.
through_model = Message.deleted.through
instances = [
through_model(message=m, user=u) for m, u in data
]
through_model.objects.bulk_create(instances)
This is supposed to be very efficient way. However if you don't care about efficiency at the moment, you can just iterate over each user and call message.deleted.add(user) for each.

How to exclude submission values from saving?

Is there a way to exclude certain submission values from being saved to the submissions table in Drupal? I would like to send a complete set of all submission values per submission via email, but I would like to exclude personal data like email-addresses and the like from being saved to the table. Is there a way to accomplish that?
You can use Drupal's webform submission exporter service to export submission data and also exclude unwanted columns during export. Something like this:
$submission_exporter = \Drupal::service('webform_submission.exporter');
$export_options = $submission_exporter->getDefaultExportOptions();
$export_options['excluded_columns'] = [
'uuid' => 'uuid',
'token' => 'token',
'webform_id' => 'webform_id',
'completed' => 'completed',
];
$submission_exporter->setWebform($webform);
$submission_exporter->setExporter($export_options);
$submission_exporter->generate();
$temp_file_path = $submission_exporter->getExportFilePath();

Django query annotate and agregate

How can I write a query like this in Django?
select sum(kolvo), reason
from(
select count(reason) kolvo, reason
from contacts group by reason) group by reason
Your query looks semantically equivalent to:
SELECT count(reason) AS kolvo,
reason
FROM contacts
GROUP BY reason
So without the outer query.
You can make such query in Django's ORM with:
Contacts.objects.values('reason').annotate(
kolvo=Count('id')
).order_by('reason')
This will then return a QuerySet that wraps dictionaries, like:
< QuerySet [
{ 'reason': 'some reason', 'kolvo': 13 },
{ 'reason': 'some other reason', 'kolvo': 2 },
] >

django_filter ModelMultipleChoiceFilter - Bad Request on miss

I have a ViewSet that has a filter_class that is like this:
class OrderFilter(django_filters.FilterSet):
......
product = django_filters.ModelMultipleChoiceFilter(
field_name='product',
queryset=Product.objects.all(),
)
With this, I can specify a product id, and make a get request like this /orders/?product=<product_id>.
I'm seeing the following behavior in three situations:
If there are order(s) with the given product_id, it will return those orders ... good!
If there are NO orders with that given product_id, it will return a successful 200, with an empty results (e.g. {count: 0, next: null, previous: null, results: [ ]} ... good!
If that product_id doesn't exist, it returns a 400 Bad Request with a ValidationError and this response:
{ product: [ "Select a valid choice. %(value)s is not one of the available choices." ]} ... wtf!
This last situation seems odd to me. Shouldn't the response be the same if (a) the given object relationship isn't there and (b) the object itself doesn't exist? This dual behavior would seemingly indicate to the FE if that product exists or not, and perhaps they shouldn't know that information.
How can I change my filters such that it always returns a 200 with a result set that is empty in those two cases?
I'm on Django==1.11.15 and django-filter==2.0.0
Thanks!

Django - remove duplicates records from DB

I want to set 'unique_together' on my DB (postgres). The problem is that I may already have duplicates on DB, so migration would probably not work. So as I see it - before the deployment I need to run some script to remove all duplications (leave only one of each). I prefer doing it with Django Custom Command.
The table 'mapping' looks something like - Id, user_id, user_role, project_id, user_type.
I want to set 'unique_together' for all of them.
And the script I use to retrieve duplicated rows is-
duplicates = (Mapping.objects.values('project_id', 'user_id', 'user_type', 'user_role').annotate(
count=Count('id')).values('project_id', 'user_id', 'user_type', 'user_role').order_by().
filter(count__gt=1))
It returns list of objects that contains the duplicated attributes.
for example:
QuerySet [{'user_id': '2222', 'user_type': '1', 'user_role': '1', 'project_id': UUID('c02bda0e-5488-4519-8f34-96b7f3d36fd6')}, {'user_id': '44444', 'user_type': '1', 'user_role': '1', 'project_id': UUID('8c088f57-ad0c-411b-bc2f-398972872324')}]>
Is there a way to retrieve the Ids directly?
Is there a better way?
You can try it:
Mapping.objects.values(
'project_id', 'user_id', 'user_type', 'user_role'
).annotate(count=Count('id')
).annotate(max_id=Max('id')
).values('max_id').order_by().filter(count__gt=1)