Is it possible to filter queryset in Django Select2 forms?
I got a form that sends a direct message to the user and I want to have the possibility to filter users.
s2forms.ModelSelect2Widget, as I see, selects all instances of User model
Now I need t to implement a flag to the User model (allow_direct_messages), and if the user allows sending direct messages, so I need to filter them accordingly.
class DirectMessageCreateForm(forms.ModelForm):
class Meta:
model = DirectMessage
fields = ("author", "recipient", "content")
labels = {
"author": "",
}
widgets = {
"recipient": UsersWidget,
"content": forms.Textarea(attrs={
'class': 'block p-3 w-full text-md bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500'
' focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 '
'dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500',
'placeholder': "Type your message here..."
}),
"author": forms.TextInput,
}
class UsersWidget(s2forms.ModelSelect2Widget):
search_fields = [
"name__icontains",
"email__icontains",
]
Maybe someone knows how to make a custom queryset inside ModelSelect2Widget form?
Thanks in advance
I found solution
Hope it will help somebody
Don`t be afraid to set a queryset attribute inside your class:
class UsersWidget(s2forms.ModelSelect2Widget):
*queryset = User.objects.exclude(profile__allow_direct_messages=False)*
search_fields = [
"name__icontains",
"email__icontains",
]
This should work!
Related
in my post requests to OrderProduct model, i want to only have to pass order.id and product.id and it works... untill i add a serializer to retrieve product.name. It might be because i didnt understand documentation about nested requests, but im unable to advance further into my project :(
[
{
"id": 2,
"order": 1,
"product": 1,
}
]
^ here's how it looks without nested serializer, and thats the data that i wanna have to input
[
{
"id": 2,
"order": 1,
"product": {
"id": 1,
"name": "gloomhaven",
},
},
^ here's how it looks after i add an additional serializer. I pretty much want these nested fields to be read only, with me still being able to send simple post requests
here are my serializers
class OrderProductSerializer(serializers.ModelSerializer):
product = Product()
class Meta:
model = OrderProduct
fields = [
"id",
"order",
"product"]
class Product(serializers.ModelSerializer):
class Meta:
model = Product
fields = (
"id",
"name")
Is there any way for me to accomplish this? Thank you for trying to help!
Just overwrite to_representation method of the serializer
def to_representation(self, instance):
response = super().to_representation(instance)
response['other_field'] = instance.id# also response['other_field'] = otherSerializer(instance.model)
return response
This can solve your problem
I think you are missing many=True
class OrderProductSerializer(serializers.ModelSerializer):
product = Product(many=True)
class Meta:
model = OrderProduct
fields = [
"id",
"order",
"product"]
{
"product_name": "CRVRVgfhghg",
"product_price": "0.01",
"product_location": "KIKUYU,KENYA",
"product_description": "VFVFVFVFVFVF",
"product_category_name": "livestock",
"product_farmer_name": "james",
"product_category_data": {
"product_category_name": "livestock",
"product_category_description": "livestock one"
},
"product_product_file_data": {
"product_file_name": "ok"
}
}
i have three tables: product_category,product and product_product_files...what i want is to populate all the three tables at once using one view and url pattern... is there a way i can do this using serializers??
I think what you are looking for is the following documentation DRF writable nested serializers.
Looking at this you'll see that they state the following:
'By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create create() and/or update() methods in order to explicitly specify how the child relationships should be saved:'
This is the example they use:
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ['order', 'title', 'duration']
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
def create(self, validated_data):
tracks_data = validated_data.pop('tracks')
album = Album.objects.create(**validated_data)
for track_data in tracks_data:
Track.objects.create(album=album, **track_data)
return album
The data they put in then should look like this:
data = {
'album_name': 'The Grey Album',
'artist': 'Danger Mouse',
'tracks': [
{'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
{'order': 2, 'title': 'What More Can I Say', 'duration': 264},
{'order': 3, 'title': 'Encore', 'duration': 159},
],
Looking at your code-snippet I would guess these models are related in a One-to-one relationship. In this case many=False which is also the default. You could do that for both models.
I think you would be able to get to the right code with this information, if not please let me know.
I need to add ID field to my form, and I'm getting so mad
Currently I have :
class ProductVideoForm(forms.ModelForm):
class Meta:
model = ProductVideo
translatable_fields = get_translatable_fields(ProductVideoTranslation)
fields = [
"product",
"id", #added!!!!
"type",
"placeholder",
] + translatable_fields
widgets = {
"placeholder": ImageInput(),
}
trans_fields_per_lang = get_trans_fields_per_lang(translatable_fields)
I added ID to fields, and the template is:
{{ video_formset.management_form }}
Why Is ID not displayed ??
actually, I just need display it, not updated.
Yea ok, but by default django will not display editable=False fields in forms.
What you are looking for is disabled param.
https://docs.djangoproject.com/en/4.1/ref/forms/fields/#disabled
I have this form and model for a group:
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = ('leader', 'description', 'size', 'max_size', 'motto')
widgets = {
'size': forms.CheckboxInput(attrs={'id': 'size'}),
'max_size': forms.TextInput(attrs={'type': 'hidden', 'id': 'maxSize'}),
}
The creator of the group has an option to check yes for size and on doing so, I used javascript to change the type of max_size to show.
In my create_group.html template:
<script>
let size = document.getElementById('size')
let maxSize = document.getElementById('maxSize')
let checked = false
size.onclick = () => {
checked = !checked
if (checked === true) {
maxSize.type = 'show'
} else {
maxSize.type = 'hidden'
}
}
</script>
Now, this works fine, the only problem is that the fields are displayed out of order.
When the page loads, max_size is false and its field is not displayed. Which is good. However, when the user checks that group has a size, and, subsequently, the max_size has a display of show, the field shows up after the motto field and not in its correct order according to fields = ('leader', 'description', 'size', 'max_size', 'motto').
Furthermore, the max_size field is included inside the motto element itself and not as its own field:
vs. the other fields which are all in their own <p></p>.
I'm guessing that {{form.as_p}} etc. render all the visible fields first, then the hidden ones.
You can explicitly render the fields in the order you want in your template. Rather than hardcoding the order in your template, maybe this (I've never tried this):
FIELD_ORDER = ( ('leader', 'description', 'size', 'max_size', 'motto')
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = FIELD_ORDER
Pass to your template a list of fields explicitly in the order you want:
fields_in_order = [ form[x] for x in FIELD_ORDER ]
In the template
{% for field in fields_in_order %}
{{field}}
{% endfor %}
Or, you can make the hiding of this field something done by JS
If anyone else comes across this issue, it's also possible to just use js and css. Right now, I'm using javascript to see if size is checked and if it is then maxSize.style.display = 'block' vs maxSize.style.display = 'none' if size isn't checked.
Then I had the issue of django's form label still being visible. To fix that I saw an answer on dev.to which you can see for yourself.
My issue now is that I don't know how to add a label that is only visible when the form field is visible.
I already have a general idea of how it should be done. The only issue that I face now is how to actually send the data. I don't want to create new Projects I just want to add them to the notifications. How do I pass the data, the actual JSON?
class NotificationsScheduleSerializer(ModelSerializer):
projects = ProjectSerializer(many=True) # Thats the Many2Many Field
user = HiddenField(default=CurrentUserDefault())
class Meta:
model = NotificationsSchedule
fields = [
"pk",
"projects",
"period",
"week_day",
"created_at",
"time",
"report_type",
"user",
]
def create(self, validated_data):
breakpoint() # I don't ever get "projects" in validated_data just Empty OrderedDict
projects_data = validated_data.pop("projects", [])
notification = NotificationsSchedule.objects.create(**validated_data)
return notification
class ProjectSerializer(ModelSerializer):
class Meta:
model = Project
fields = ["pk", "name"]
I want to be able to pass something like this.
{
"projects": [290, 289],
"period": "daily",
"week_day": 2,
"time": "16:02:00",
"report_type": "word_report"
}
But it expects dict instead.
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
You have to set read_only,
projects = ProjectSerializer(many=True, read_only=True)
And when creating Notifications ,
notification = NotificationsSchedule.objects.create(**validated_data)
notification.projects.add(*self.initial_data.get("projects"))
notification.save()