How do I set a field's read-only attribute? - django

I am trying to set a readonly attribute to this specific field in django. What I hope to accomplish is the field's data shows but it cant be changed. How do I go about this? Below is what I've tried and isn't working for me.
class editcartform(ModelForm):
class Meta:
model = signedup
exclude = ['sessionid', 'price', 'paid', 'orderid', 'dancer_1_level', 'dancer_2_level', 'dancer_2_sex', 'dancer_1_sex']
widgets = {
'comp_name':Textarea(attrs={'readonly'}),
}

'comp_name':Textarea(attrs={'readonly':'readonly'}),
or
'comp_name':Textarea(attrs={'disabled':'disabled'}),
It may change depends on what you want.

Related

How to change form field attribute in the view?

I have a model form containing four fields, by default only one field is active waiting for the user input.
class SaleOrderInvoiceForm(forms.ModelForm):
class Meta:
model = SaleOrderInvoice
fields = (
'supplier',
'sale_order',
'invoice_number',
'invoice_date',
)
widgets={
'supplier':forms.Select(attrs={'class':'form-control'}),
'sale_order':forms.Select(attrs={'class':'form-control', 'disabled':'disabled'}),
'invoice_number':forms.TextInput(attrs={'class':'form-control', 'disabled':'disabled'}),
'invoice_date':forms.DateInput(attrs={'class':'form-control','type':'date','disabled':'disabled'}),
}
Based on the user selection of the first input, the rest of the form is loaded via AJAX. Now in the view, after necessary filtering, I want to swtich the disabled form fields to enabled and the enabled to disabled. I have tried the following but with no luck.
form = SaleOrderInvoiceForm()
form.fields['supplier'].disabled = True
form.fields['sale_order'].disabled = False
form.fields['invoice_number'].disabled = False
form.fields['invoice_date'].disabled = False
It neither raises any exceptions nor it works as desired.
can anyone suggest a way forward with this?
I found the solution, it appears the disabled attribute cannot be reset back to False, it can however pop out.
form.fields['supplier'].disabled = 'disabled'
form.fields['sale_order'].widget.attrs.pop('disabled')
form.fields['invoice_number'].widget.attrs.pop('disabled')
form.fields['invoice_date'].widget.attrs.pop('disabled')

filters.FilterSet with a custom field that is not in the model

class SomeFilter(filters.FilterSet):
id = NumberInFilter(field_name='id')
name = StringInFilter(field_name='name')
custom_field_that_is_not_in_model = filters.CharFilter()
This displays "[invalid name]:" because field custom_field_that_is_not_in_model is not in the Model (other fields work fine). How can I make it display what I want? I am going to call a custom method on this field.
Use label---(django-filter doc) option
class SomeFilter(filters.FilterSet):
field_in_model = filters.CharFilter(label='Your Cute Name')

DateField in DRF django

I've a model which have a DateField.
class A(model.Model):
a = model.DateField()
class SerializerA(serializers.ModelSerializer):
class Meta:
model = A
fields = (a,)
The payload that I pass have a chance that it might send only year, for eg:-
{
"a": "1991"
}
It returns an error saying,
"Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]."
I'm already passing one the format, as mentioned in the error, but still I'm getting an error.
Why?
One of the simple solutions will be, define field a as separate in your serializer and provide sufficient values to the input_formats argument
required_formats = ['%Y', '%d-%m-%Y'] # add other formats you need
class SerializerA(serializers.ModelSerializer):
a = serializers.DateField(input_formats=required_formats)
class Meta:
model = A
fields = ('a',)
You need to set all needed date formats to variable DATE_INPUT_FORMATS in settings.py, for example:
DATE_INPUT_FORMATS = ['%d-%m-%Y']

Django Rest Framework - Updating a foreign key

I am a bit frustrated with this problem using the Django Rest Framework:
I am using a viewset, with a custom serializer. This serializer has its depth set to 1. When i query this viewset I get the correct representation of data for example:
data = {
id: 1,
issue_name: 'This is a problem',
status: {
id: 3,
name: 'todo'
}
}
The problem comes in when I need to update the status. For example if I want to select another status for this issue, for example:
status_new = {
id: 4,
name: 'done'
}
I send the following PATCH back to the server, this is the output:
data = {
id: 1,
issue_name: 'This is a problem',
status: {
id: 4,
name: 'done'
}
}
However, the status does not get updated. Infact, it is not even a part of the validated_data dictionary. I have read that nested relations are read-only. Could someone please tell me what I need to do this in a simple way?
Would really be obliged.
Thanks in advance
As stated in the documentation, you will need to write your own create() and update() methods in your serializer to support writable nested data.
You will also need to explicitly add the status field instead of using the depth argument otherwise I believe it won't be automatically added to validated_data.
EDIT: Maybe I was a bit short on the details: what you want to do is override update in ModelIssueSerializer. This will basically intercept the PATCH/PUT requests on the serializer level. Then get the new status and assign it to the instance like this:
class StatusSerializer(serializers.ModelSerializer):
class Meta:
model = Status
class ModelIssueSerializer(serializers.ModelSerializer):
status = StatusSerializer()
# ...
def update(self, instance, validated_data):
status = validated_data.pop('status')
instance.status_id = status.id
# ... plus any other fields you may want to update
return instance
The reason I mentioned in the comment that you might need to add a StatusSerializer field is for getting status into validated_data. If I remember correctly, if you only use depth then nested objects might not get serialized inside the update() / create() methods (although I might be mistaken on that). In any case, adding the StatusSerializer field is just the explicit form of using depth=1
I usually use custom field for such cases.
class StatusField(serializers.Field):
def to_representation(self, value):
return StatusSerializer(value).data
def to_internal_value(self, data):
try:
return Status.objects.filter(id=data['id']).first()
except (AttributeError, KeyError):
pass
And then in main serializer:
class IssueSerializer(serializers.ModelSerializer):
status = StatusField()
class Meta:
model = MyIssueModel
fields = (
'issue_name',
'status',
)
I would assume that your models mimic your serializer's data. Also, I would assume that you have a one to many relation with the status(es) but you don't need to create them via the issue serializer, you have a different endpoint for that. In such a case, you might get away with a SlugRelatedField.
from rest_framework import serializers
class StatusSerializer(serializers.ModelSerializer):
class Meta:
model = MyStatusModel
fields = (
'id',
'status',
)
class IssueSerializer(serializers.ModelSerializer):
status = serializers.SlugRelatedField(slug_field='status', queryset=MyStatusModel.objects.all())
class Meta:
model = MyIssueModel
fields = (
'issue_name',
'status',
)
Another valid solution would be to leave here the foreign key value and deal with the display name on the front-end, via a ui-select or select2 component - the RESTfull approach: you are handling Issue objects which have references to Status objects. In an Angular front-end app, you would query all the statuses from the back-end on a specific route and then you will display the proper descriptive name based on the foreign key value form Issue.
Let me know how is this working out for you.

How to access Django's field.choices?

Is there a way (without using a form) to access a model fields choices value?
I want to do something like field.choices and get the list of values either in a view or template.
Sure. Just access the choices attribute of a Model field:
MyModel._meta.get_field('foo').choices
my_instance._meta.get_field('foo').choices
If you're declaring your choices like this:
class Topic(models.Model):
PRIMARY = 1
PRIMARY_SECONDARY = 2
TOPIC_LEVEL = ((PRIMARY, 'Primary'),
(PRIMARY_SECONDARY, 'Primary & Secondary'),)
topic_level = models.IntegerField('Topic Level', choices=TOPIC_LEVEL,
default=1)
Which is a good way of doing it really. See: http://www.b-list.org/weblog/2007/nov/02/handle-choices-right-way/
Then you can get back the choices simply with Topic.TOPIC_LEVEL
I think you are looking for get_fieldname_display() function.