I created a form using mysql EnumField but this field does not appear on the HTML page when it is created.
forms.py - see transaction_type_1 that does not appear on he HTML page.
from django import forms
from .models import tledger_account, tfirst_free_number
from django_mysql.models import EnumField
from datetime import datetime
class Transactions(forms.Form):
transaction_no = forms.IntegerField(initial=1)
description = forms.CharField(max_length=50)
transaction_date = forms.DateField()
sequence_1 = forms.IntegerField()
transaction_type_1 = EnumField(choices=['Debit','Credit'])
ledger_account_1 = forms.ModelChoiceField(queryset=tledger_account.objects.filter(active='Yes'))
amount_1 = forms.DecimalField(max_digits=14, decimal_places=2)
sequence_2 = forms.IntegerField()
transaction_type_2 = EnumField(choices=['Debit', 'Credit'], default='Debit')
ledger_account_2 = forms.ModelChoiceField(queryset=tledger_account.objects.filter(active='Yes'))
amount_2 = forms.DecimalField(max_digits=14, decimal_places=2)
def __init__(self, *args, **kwargs):
# Get 'initial' argument if any
#initial_arguments = kwargs.get('initial', None)
updated_initial = {}
updated_initial['transaction_no'] = tfirst_free_number.objects.get(id=1).ffn
updated_initial['transaction_date'] = datetime.today
updated_initial['sequence_1'] = 1
updated_initial['sequence_2'] = 2
updated_initial['transaction_type_1'] = 'debit'
kwargs.update(initial=updated_initial)
super(Transactions, self).__init__(*args, **kwargs)
You are trying to use a model thus the form does not really know how to display it. You should declare a valid form field type that would be then put in the database or use something like this:
https://pypi.org/project/django-enumfield/
Related
I need help to convert the below class-based LISTVIEW to the rest framework LISTAPIVIEW because I want to authenticate users using permission classes simple JWT authentication.
thanks
class MessagesModelList(ListView):
http_method_names = ['get', ]
paginate_by = getattr(settings, 'MESSAGES_PAGINATION', 500)
def get_queryset(self):
if self.kwargs.get('dialog_with'):
qs = MessageModel.objects \
.filter(Q(recipient=self.request.user, sender=self.kwargs['dialog_with']) |
Q(sender=self.request.user, recipient=self.kwargs['dialog_with'])) \
.select_related('sender', 'recipient')
else:
qs = MessageModel.objects.filter(Q(recipient=self.request.user) |
Q(sender=self.request.user)).prefetch_related('sender', 'recipient', 'file')
return qs.order_by('-created')
def render_to_response(self, context, **response_kwargs):
user_pk = self.request.user.pk
data = [serialize_message_model(i, user_pk)
for i in context['object_list']]
page: Page = context.pop('page_obj')
paginator: Paginator = context.pop('paginator')
return_data = {
'page': page.number,
'pages': paginator.num_pages,
'data': data
}
return JsonResponse(return_data, **response_kwargs)
you need to create the below files and and try the following
serializer.py
from rest_framework import serializers
from rest_framework.pagination import PageNumberPagination
from .models import MessageModel
class CustomPagination(PageNumberPagination):
page_size = 500
page_size_query_param = 'page_size'
max_page_size = 10000
class MessagesModelSerializer(serializers.ModelSerializer):
class Meta:
model = MessageModel
fields = "__all__" #use this if you want all the fields of your model available
fields = ['field1','field2', etc..] #use this if you want specific fields of your model
views.py
from .models import MessageModel
from rest_framework import mixins,viewsets
from .serializer import MessagesModelSerializer,CustomPagination
class MessagesModelListView(mixins.ListModelMixin,
viewsets.GenericViewSet):
serializer_class = MessagesModelSerializer
pagination_class = CustomPagination
def get_queryset(self):
if self.kwargs.get('dialog_with'):
qs = MessageModel.objects \
.filter(Q(recipient=self.request.user, sender=self.kwargs['dialog_with']) |
Q(sender=self.request.user, recipient=self.kwargs['dialog_with'])) \
.select_related('sender', 'recipient')
else:
qs = MessageModel.objects.filter(Q(recipient=self.request.user) |
Q(sender=self.request.user)).prefetch_related('sender', 'recipient', 'file')
return qs.order_by('-created')
urls.py
from .views import MessageModelListView
from rest_framework.routers import DefaultRouter
router=DefaultRouter()
router.register('messages', MessagesModelList, 'messages')
This is a basic example of how you can create ListView using DRF. if you need more customization, please refer the documentation : https://www.django-rest-framework.org/api-guide/generic-views/
By referring to this article, I was able to implement the method of and search.
Django-filter with DRF - How to do 'and' when applying multiple values with the same lookup?
I want to know how to do or search for multiple keywords using the same field. How can I implement it?
Here is the code:
from rest_framework import viewsets
from django_filters import rest_framework as filters
from .serializers import BookInfoSerializer
from .models import BookInfo
class MultiValueCharFilter(filters.BaseCSVFilter, filters.CharFilter):
def filter(self, qs, value):
# value is either a list or an 'empty' value
values = value or []
for value in values:
qs = super(MultiValueCharFilter, self).filter(qs, value)
return qs
class BookInfoFilter(filters.FilterSet):
title = MultiValueCharFilter(lookup_expr='contains')
# title = MultiValueCharFilter(lookup_expr='contains', conjoined=False) -> get an error
class Meta:
model = BookInfo
fields = ['title']
class BookInfoAPIView(viewsets.ReadOnlyModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
filter_class = BookInfoFilter
if I set conjoined=False like this title = MultiValueCharFilter(lookup_expr='contains', conjoined=False) get an error __init__() got an unexpected keyword argument 'conjoined'
Django 3.2.5
django-filter 2.4.0
djangorestframework 3.12.4
Python 3.8.5
You can try to modify the queryset returned from MultiValueCharFilter
and combine values with operator.
example:
import operator
from functools import reduce
class MultiValueCharFilter(BaseCSVFilter, CharFilter):
def filter(self, qs, value):
expr = reduce(
operator.or_,
(Q(**{f'{self.field_name}__{self.lookup_expr}': v}) for v in value)
)
return qs.filter(expr)
class BookInfoFilter(filters.FilterSet):
title = MultiValueCharFilter(lookup_expr='contains')
class Meta:
model = BookInfo
fields = ['title']
You can try to create a new Class called ListFilter like this:
from django_filters.filters import Filter
from django_filters.conf import settings as django_filters_settings
from django.db.models.constants import LOOKUP_SEP
class ListFilter(Filter):
"""
Custom Filter for filtering multiple values.
"""
def __init__(self, query_param: str, *args, **kwargs):
"""
Override default variables.
Args:
query_param (str): Query param in URL
"""
super().__init__(*args, **kwargs)
self.query_param = query_param
self.distinct = True
def filter(self, qs, value):
"""
Override filter method in Filter class.
"""
try:
request = self.parent.request
values = request.query_params.getlist(self.query_param)
# Remove empty value in array
values = list(filter(None, values))
except AttributeError:
values = []
# Filter queryset by using OR expression when lookup_expr is 'in'
# Else filter queryset by using AND expression
if values and self.lookup_expr == 'in':
return super().filter(qs, values)
for value in set(values):
predicate = self.get_filter_predicate(value)
qs = self.get_method(qs)(**predicate)
return qs.distinct() if self.distinct else qs
def get_filter_predicate(self, value):
"""
This function helps to get predicate for filtering
"""
name = self.field_name
if (
name
and self.lookup_expr != django_filters_settings.DEFAULT_LOOKUP_EXPR
):
name = LOOKUP_SEP.join([name, self.lookup_expr])
return {name: value}
My class above supports filter nested fields using both OR and AND expressions. E.g. about filter title field using OR expression:
class BookInfoFilter(filters.FilterSet):
title = ListFilter(query_param='title')
class Meta:
model = BookInfo
fields = ['title']
I have an object, whose attributes I would like to update. Right now, I am able to update a single attribute eg: name. But the object has several attributes which include name, contact_name, contact_email and contact_phone_number.The following is what I have at the moment.
In views.py
class MerchantViewSet(GetPrefixedIDMixin, viewsets.ModelViewSet):
"""POST support for /merchants/."""
print ("in MerchantViewSet")
queryset = models.Merchant.objects.all()
serializer_class = serializers.CreateMerchantSerializer
lookup_field = "id"
# lookup_value_regex = f"{models.Merchant.id_prefix}_[a-f0-9]{32}"
lookup_value_regex = ".*"
permission_classes = [permissions.MerchantPermission]
def get_queryset(self):
"""Filter the queryset based on the full merchant name or starting with a letter."""
queryset = models.Merchant.objects.all()
search_param = self.request.query_params.get("search", None)
if search_param:
if search_param.startswith("^"):
queryset = queryset.filter(name__istartswith=search_param[1:])
else:
queryset = queryset.filter(name__icontains=search_param)
return queryset
def update(self, request, *args, **kwargs):
request.data["user"] = request.user.id
print (self.get_object().name)
print (request.data)
merchant = self.get_object()
print (response)
print (merchant.name)
for merchants in models.Merchant.objects.all():
print (merchants.id)
if (merchants.id == merchant.id):
merchants.name = request.data["name"]
merchants.save()
I've tried using
response = super(MerchantViewSet, self).update(request, *args, **kwargs)
from what I read in the DRF documentations but using this just returns error 404 when I run my test. Do I simply have to do with I did with name in my code above, with the other attributes? Or is there a more streamlined way to do this?
This is my test:
class MerchantsViewSetTest(tests.BigETestCase): # noqa
#classmethod
def setUpClass(cls): # noqa
super(MerchantsViewSetTest, cls).setUpClass()
cls.application = tests.get_application()
tests.create_group("merchant")
cls.consumer_user = tests.create_consumer()
cls.admin = tests.create_administrator()
cls.merchant_geraldine = models.Merchant.objects.create(
name="Test Account 1",
contact_name="Geraldine Groves",
contact_email="geraldine#example.com",
contact_phone_number="+35310000000",
)
cls. merchant_barbara = models.Merchant.objects.create(
name="Account 2",
contact_name="Barbara",
contact_email="barbara#example.com",
contact_phone_number="+35310000432",
)
def test_edit_merchant(self): # noqa
url = reverse("bige_transactions:merchant-detail", kwargs={'id': self.merchant_geraldine.prefixed_id})
# payload
data = {"name": "Edited"}
# Put data
resp_data = self.put(url, data, user=self.admin, status_code=200)
# Check if data was updated
url = reverse("bige_transactions:merchant-list")
# Try without authenticated user
self.get(url, status_code=401)
# Try with authenticated admin user
resp_data = self.get(url, user=self.admin, status_code=200)
print (resp_data)
In my app, i have a chained dropdown in which i am getting the second dropdown via jquery ajax, which works well.So i am trying to edit this saved data and load it back to an edit form, but the dropdown is showing empty. This is what i have done so far
Here is my model.py
class SchoolFees(models.Model):
fid = models.ForeignKey(FacultyData, on_delete= models.SET_NULL, null=True)
did = models.ForeignKey(DepartmentData, on_delete= models.SET_NULL, null=True)
sid = models.ForeignKey(SessionData, on_delete= models.SET_NULL, null=True)
amount = models.CharField(max_length=30)
def __str__(self):
return self.amount
forms.py
class FeesCreationForm(forms.ModelForm):
fid = forms.ModelChoiceField(queryset=FacultyData.objects.all(), empty_label="--Select Faculty--",
widget=forms.Select(attrs={'class': 'form-control'}))
did = forms.ModelChoiceField(queryset=DepartmentData.objects.all(), empty_label="--Select Faculty First--",
widget=forms.Select(attrs={'class': 'form-control'}))
sid = forms.ModelChoiceField(queryset=SessionData.objects.all(), empty_label="--Select Session--",
widget=forms.Select(attrs={'class': 'form-control'}))
class Meta:
model = models.SchoolFees
fields = ['sid', 'fid', 'did', 'amount']
widgets = {
'amount': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter Amount'})
}
def __init__(self, *args, **kwargs):
super(FeesCreationForm, self).__init__(*args, **kwargs)
self.fields['did'].queryset = DepartmentData.objects.none()
# Get did queryset for the selected fid
if 'fid' in self.data:
try:
fd = int(self.data.get('fid'))
self.fields['did'].queryset = DepartmentData.objects.filter(fid_id=fd).order_by('id')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and use empty queryset
Here is my view.py
def edit_fee(request, pk):
app = settings.CONFIG
post = get_object_or_404(SchoolFees, pk=pk)
if request.method == 'POST':
form = FeesCreationForm(request.POST, instance=post)
if form.is_valid():
form.save()
messages.add_message(request, messages.WARNING, "Fees record updated successfully")
return redirect('bursary:create_fee')
else:
# bring edit form out
form = FeesCreationForm(instance=post)
table = FeesTable(SchoolFees.objects.all())
RequestConfig(request, paginate={'per_page': 10}).configure(table)
context = {"form": form, "fees": table, 'app': app}
return render(request, 'editfee.html', context)
I expect that the saved value is pass to the dropdown with other form fields which are already showing
After going through this post, i was able to solve it when reading the comments. All i needed was to add a backward relationship to my init function.
class FeesCreationForm(forms.ModelForm):
fid = forms.ModelChoiceField(queryset=FacultyData.objects.all(), empty_label="--Select Faculty--",
widget=forms.Select(attrs={'class': 'form-control'}))
did = forms.ModelChoiceField(queryset=DepartmentData.objects.all(), empty_label="--Select Faculty First--",
widget=forms.Select(attrs={'class': 'form-control'}))
sid = forms.ModelChoiceField(queryset=SessionData.objects.all(), empty_label="--Select Session--",
widget=forms.Select(attrs={'class': 'form-control'}))
class Meta:
model = models.SchoolFees
fields = ['sid', 'fid', 'did', 'amount']
widgets = {
'amount': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter Amount'})
}
def __init__(self, *args, **kwargs):
super(FeesCreationForm, self).__init__(*args, **kwargs)
self.fields['did'].queryset = DepartmentData.objects.none()
# Get did queryset for the selected fid
if 'fid' in self.data:
try:
fd = int(self.data.get('fid'))
self.fields['did'].queryset = DepartmentData.objects.filter(fid_id=fd).order_by('id')
except (ValueError, TypeError):
pass # invalid input from the client; ignore and use empty queryset
elif self.instance.pk:
self.fields['did'].queryset = self.instance.fid.departmentdata_set.order_by('id')
#backward relation - for this faculty selected, check its deparm
#every department has its faculty
# #in other word, which dept has their foreign key pointing to the current instance of faculty
I'm following this link (http://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers) to write nested serializer. But when I pop the 'vars' from validated_data in the create method of HostSerializer, I found it's empty.
I'm using django 1.9.2 and django restframework 3.3.2.
My model:
class Host(models.Model):
name = CharField(max_length=20, primary_key=True)
vm_cpu = IntegerField(default=2)
vm_mem = IntegerField(default=2048)
create_vm = BooleanField(default=True)
def __unicode__(self):
return('%s' % (self.name))
class Variable(models.Model):
name = CharField(max_length=10)
value = CharField(max_length=20)
host = models.ForeignKey(Host, related_name='vars')
def __unicode__(self):
return('%s=%s' % (self.name, self.value))
Serializer
class VariableSerializer(ModelSerializer):
class Meta:
model = Variable
class HostSerializer(ModelSerializer):
vars = VariableSerializer(many=True)
class Meta:
model = Host
def create(self, validated_data):
# i set a break point here and found vars_data is empty
vars_data = validated_data.pop('vars')
host = Host.objects.create(**validated_data)
for v in vars_data:
Variable.objects.create(host = host, **v)
return host
This is the problem I found vars_data is an empty list:
def create(self, validated_data):
# i set a break point here and found vars_data is empty
vars_data = validated_data.pop('vars')
Here's the rest of the code
admin.py
class VariableAdmin(admin.ModelAdmin):
list_display = ['name', 'value']
class HostAdmin(admin.ModelAdmin):
list_display = ['name']
admin.site.register(Variable, VariableAdmin)
admin.site.register(Host, HostAdmin)
urls.py
router = DefaultRouter()
router.register(r'variables', VariableViewSet, base_name='variables')
router.register(r'hosts', HostViewSet, base_name='hosts')
urlpatterns = [
url(r'^', include(router.urls)),
]
views.py
class VariableViewSet(ModelViewSet):
queryset = Variable.objects.all()
serializer_class = VariableSerializer
class HostViewSet(ModelViewSet):
queryset = Host.objects.all()
serializer_class = HostSerializer
My test program
post.py
import json
import requests
file = 'host.json'
url = 'http://localhost:8001/test_nest/hosts/'
with open(file, 'r') as f:
j = f.read()
data = json.loads(j)
r = requests.post(url, data = data)
print r.text
And here's the test data
host.json
{
"name": "host4",
"vars": [
{
"name": "var2-a",
"value": "a1"
},
{
"name": "var2-b",
"value": "a2"
}
],
"vm_cpu": 2,
"vm_mem": 2048,
"create_vm": true
}
I'm new to django. So I'm wondering if it's something simple and obvious. Did I use the wrong viewset? Did I post to the wrong URL? Or I setup the URL structure wrong?
I your serializers try using...
def update(self, instance,validated_data):
instance.vars_data = validated_data.get('vars',instance.vars)
instance.host = Host.objects.create(**validated_data)
for v in vars_data:
v,created=Variable.objects.create(host = host, **v)
instance.v.add(v)
return host
The following code works for me. Maybe you can try it:
models.py
class UserProfile(AbstractUser):
pass
class TobaccoCert(models.Model):
license = models.CharField(primary_key=True, max_length=15)
license_image = models.ImageField(upload_to="certs", blank=True, null=True, verbose_name='cert image')
views.py
class UserViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
serializer_class = UserRegSerializer
...
def create(self, request, *args, **kwargs):
data = request.data
# here i make my nested data `certs` and update it to data
image_data = request.FILES.get('images')
_license = data.get('username', None)
certs = {'license': _license, 'license_image': image_data}
data['certs'] = certs
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
user = self.perform_create(serializer) # save it
ret_dict = serializer.data
payload = jwt_payload_handler(user)
ret_dict["token"] = jwt_encode_handler(payload) # 获取token
ret_dict["name"] = user.name if user.name else user.username
headers = self.get_success_headers(serializer.data)
return Response(ret_dict, status=status.HTTP_201_CREATED, headers=headers)
serializer.py
class CertSerializer(serializers.ModelSerializer):
pass
class UserRegSerializer(serializers.ModelSerializer):
# [Serializer relations - Django REST framework](https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers)
# this line is vars of your code
certs = CertSerializer(many=True,write_only=True)
...
password = serializers.CharField(
style={'input_type': 'password'}, help_text="passowrd", label="passowrd", write_only=True,
)
class Meta:
model = User
fields = ("username", "mobile", "password", 'province', 'city', 'dist', 'code', 'certs')
def create(self, validated_data):
"""
"""
# here we get certs
certs_data = self.initial_data.get('certs')
# just pop certs because it is useless for user create
certs_empty = validated_data.pop('certs')
user = super().create(validated_data=validated_data)
user.set_password(validated_data["password"])
user.save()
# use certs data to create cert
image_url = self.save_image(certs_data)
_license = validated_data.get('username', None)
TobaccoCert.objects.create(license=_license, license_image=image_url)
return user
In short, you need to add your nested parameters directly in the request parameters. In my example code, it is certs which is vars in your example and then use certs_data = self.initial_data.get('certs') get the parameters you passed in create method.
Another possible way is post your data before your requests:
In postman:
enter image description here
in request:
python - Django Rest Framework writable nested serializer with multiple nested objects - Stack Overflow
In addition, you can try to modify queryDict directly. You can refer to this link and here
Some useful links
django - Need help understanding many and source fields in a serializer - Stack Overflow
python - Django rest framework writeable nested serializer data missing from validated_data - Stack Overflow