I want to write a view to reset some model field values to their default. How can I get default model field values?
class Foo(models.Model):
bar_field = models.CharField(blank=True, default='bar')
so what I want is:
def reset(request, id):
obj = get_object_or_404(Foo, id=id)
obj.bar_field = # logic to get default from model field
obj.save()
...
Since Django 1.10: myfield = Foo._meta.get_field('bar_field').get_default()
see here (not explained in the Docs apparently...) : https://github.com/django/django/blob/master/django/db/models/fields/init.py#L2301
myfield = Foo._meta.get_field_by_name('bar_field')
and the default is just an attribute of the field:
myfield.default
Related
There is this disabled attribute. But i am not able to apply it to the modelform fields. I am not sure how to. I can add it to forms.Form easily. But since I am using widgets I just dont know where to insert it.
https://docs.djangoproject.com/en/2.0/ref/forms/fields/#disabled
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = ['date']
widgets = {'date': forms.TextInput(attrs={'readonly': 'readonly'})}
I was facing a situation when I wanted to disable some fields when creating . And some fields disabled when editing.
My Env: Python 3, Django 2.1
My Form:
class AddInvoiceForm(forms.ModelForm):
disabled_fields = ['inv_type', 'report', 'subsidiary']
class Meta:
model = models.Invoice
fields = ('inv_type', 'report', 'subsidiary', 'rate_card', 'reviewed')
def __init__(self, *args, **kwargs):
super(AddInvoiceForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
for field in self.disabled_fields:
self.fields[field].disabled = True
else:
self.fields['reviewed'].disabled = True
Try something like this, assuming that your date field is forms.DateField and that you want to use TextInput widget:
class TestForm(forms.ModelForm):
date = forms.DateField(widget=forms.TextInput, disabled=True)
class Meta:
model = Test
fields = ['date']
This will override the default field definition which is created from your Test model definition.
The disabled boolean argument, when set to True, disables a form field using the disabled HTML attribute so that it won’t be editable by users. Even if a user tampers with the field’s value submitted to the server, it will be ignored in favor of the value from the form’s initial data.
Read about readonly vs disabled HTML input attributes.
The key note to take out from the above SO post is:
A readonly element is just not editable, but gets sent when the according form submits. a disabled element isn't editable and isn't sent on submit.
From above quote, setting disabled=True is enough, so you dont need to set readonly attribute on your widget.
class TestForm(forms.ModelForm):
date = forms.CharField(disabled=True)
class Meta:
model = Test
fields = ['date']
widgets = {
'date': forms.TextInput(attrs={'readonly': 'readonly'}),
}
I have two models, Tag and TagGroup.
class TagGroup(models.Model):
tag_group_name = models.CharField(max_length=100)
class Tag(models.Model):
tag_name = models.CharField(max_length=100)
tag_group = models.ForeignKey(TagGroup, blank=True, null=True)
I've put a TagGroup form as a choice field into a template so that I can assign a TagGroup to a Tag. I created this form to populate from a TagGroup queryset.
class TagGroupForm(ModelForm):
tag_group_name = forms.ModelChoiceField(queryset=TagGroup.objects.values_list('id', 'tag_group_name'), required=False)
class Meta:
model = TagGroup
fields = [
'tag_group_name'
]
I haven't seen any obvious instructions how I can assign the Id to the Tag table while showing the user only the Tag value in the choice field in the template.
Currently the above shows:
Couple of questions:
is the queryset correct? I have tried without "values_list" but it then just shows an "Object" in the form field in template?
how do i 'hide' the Id so i can save on it, while only showing the user the actual string value in the form field?
Edited to add updated form:
class TagGroupForm(ModelForm):
tag_group_name = forms.ModelChoiceField(queryset=TagGroup.objects.all(), to_field_name = 'tag_group_name', required=False)
class Meta:
model = TagGroup
fields = [
'tag_group_name'
]
this now produces the following .. looks close .. the form value has a nice string but the actual value displayed to user is still "TagGroup object". How to get this to show?
From the docs,
The str (unicode on Python 2) method of the model will be called to generate string representations of the objects for use
So simply just assign this to the objects name and all will be ok! (Also, you don't need to use values_list) The reason it shows the Object by default is because this is what the default string representation is.
class TagGroup(models.Model):
tag_group_name = models.CharField(max_length=100)
def __str__(self):
return self.tag_group_name
tag_group_name = forms.ModelChoiceField(queryset=TagGroup.objects.all(), required=False)
Alternatively, if you don't wish to modify this and wish to reserve it for other uses.
To provide customized representations, subclass ModelChoiceField and override label_from_instance. This method will receive a model object, and should return a string suitable for representing it
class TagChoiceField(ModelChoiceField):
queryset = TagGroup.objects.all()
def label_from_instance(self, obj):
return obj.tag_group_name # or similar
I have a model:
class XCall(models.Model):
created_on = models.DateTimeField(auto_now_add=True)
send_on = models.DateTimeField(default=datetime.now)
recipient = models.ForeignKey(User)
text = models.CharField(max_length=4096)
backup_calls = models.IntegerField(blank=True, null=True)
And a serializer for that model:
class CallSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='call-detail',
)
# some validation and custom field definitions
...
class Meta:
model = XCall
fields = ('url', 'id', 'text', 'recipient', 'send_on', 'backup_calls', 'status')
lookup_field= 'pk'
And here's the list view:
class CallList(generics.ListCreateAPIView):
serializer_class = CallSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrSuperuser,)
def pre_save(self, obj):
auth_user = self.request.user
obj.auth_user = auth_user
def get_queryset(self):
"""
This view should return a list of all the calls
for the currently authenticated user.
"""
auth = self.request.user
if isinstance(auth, AnonymousUser):
return []
elif auth.is_superuser:
return XCall.objects.all()
else:
return XCall.objects.filter(auth_user=auth)
In CallList's browseable API, I see the following in the POST form at the bottom:
My question is: why is there no default value set for send_on, and there is one for backup_calls? I assumed that the form would follow the XCall model specification and use datetime.now() for defaulting the former, and leave backup_calls blank (since it's nullable). How can I get the form to follow the model specifications?
You actually want to set an initial value, not a default value. See the docs. Your code should be:
from django.utils import timezone
class CallSerializer(serializers.HyperlinkedModelSerializer):
send_on = serializers.DateTimeField(initial=timezone.now())
...
A default value is the value provided to an attribute if no value is set for the field. The distinction between initial and default arguments mirrors the difference between Django's initial argument for form fields and Django's default argument for model fields.
There is a distinction between model defaults and initial values in forms. This is especially the case for default values which are actually functions because they are only called when the instance is saved. For example, which now do you want - this time at which the blank form is displayed, or the time at which the user presses "POST"? Django applies the default when saving the model if the field value is missing. To achieve what you want you need to manually set the default in the serialize field, for example:
class CallSerializer(serializers.HyperlinkedModelSerializer):
send_on = serializers.DateTimeField(default=datetime.now)
...
Can I get type of related field from a model queryset?
Let consider example model:
class Semester(models.Model):
active = models.BooleanField(default=False, verbose_name="Active")
class Subject(models.Model):
name = models.CharField(max_length=100, verbose_name="Name")
semester = models.ForeignKey(Semester, verbose_name="Semester")
if I have some field name in variable and queryset I can do this:
querySet = Subject.objects.all()
some_field_name = 'name'
field_type = querySet.model._meta.get_field(some_field_name).get_internal_type()
Is there any way to get related field type, for example:
querySet = Subject.objects.all()
some_field_name = 'semester__active'
field_type = ?
Try using get_field_by_name:
field_type = querySet.model._meta.get_field_by_name(some_field_name).get_internal_type()
From Django's source code:
def get_field_by_name(self, name):
"""
Returns the (field_object, model, direct, m2m), where field_object is
the Field instance for the given name, model is the model containing
this field (None for local fields), direct is True if the field exists
on this model, and m2m is True for many-to-many relations. When
'direct' is False, 'field_object' is the corresponding RelatedObject
for this field (since the field doesn't have an instance associated
with it).
Uses a cache internally, so after the first access, this is very fast.
"""
Also try:
field = querySet.model._meta.get_field_by_name("semester")
field_type = field[0].rel.to._meta.get_field_by_name("active").get_internal_type()
Thanks for any help!
I find solution with some help from this answer:
main, related = some_field_name.split("__")
field_type = querySet.model._meta.get_field(main).rel.to._meta.get_field(related).get_internal_type()
Even though a field is marked as 'editable=False' in the model, I would like the admin page to display it. Currently it hides the field altogether.. How can this be achieved ?
Use Readonly Fields. Like so (for django >= 1.2):
class MyModelAdmin(admin.ModelAdmin):
readonly_fields=('first',)
Update
This solution is useful if you want to keep the field editable in Admin but non-editable everywhere else. If you want to keep the field non-editable throughout then #Till Backhaus' answer is the better option.
Original Answer
One way to do this would be to use a custom ModelForm in admin. This form can override the required field to make it editable. Thereby you retain editable=False everywhere else but Admin. For e.g. (tested with Django 1.2.3)
# models.py
class FooModel(models.Model):
first = models.CharField(max_length = 255, editable = False)
second = models.CharField(max_length = 255)
def __unicode__(self):
return "{0} {1}".format(self.first, self.second)
# admin.py
class CustomFooForm(forms.ModelForm):
first = forms.CharField()
class Meta:
model = FooModel
fields = ('second',)
class FooAdmin(admin.ModelAdmin):
form = CustomFooForm
admin.site.register(FooModel, FooAdmin)
Add the fields you want to display on your admin page.
Then add the fields you want to be read-only.
Your read-only fields must be in fields as well.
class MyModelAdmin(admin.ModelAdmin):
fields = ['title', 'author', 'published_date', 'updated_date', 'created_date']
readonly_fields = ('updated_date', 'created_date')
You could also set the readonly fields as editable=False in the model (django doc reference for editable here). And then in the Admin overriding the get_readonly_fields method.
# models.py
class MyModel(models.Model):
first = models.CharField(max_length=255, editable=False)
# admin.py
class MyModelAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
return [f.name for f in obj._meta.fields if not f.editable]
With the above solution I was able to display hidden fields for several objects but got an exception when trying to add a new object.
So I enhanced it like follows:
class HiddenFieldsAdmin(admin.ModelAdmin):
def get_readonly_fields(self, request, obj=None):
try:
return [f.name for f in obj._meta.fields if not f.editable]
except:
# if a new object is to be created the try clause will fail due to missing _meta.fields
return ""
And in the corresponding admin.py file I just had to import the new class and add it whenever registering a new model class
from django.contrib import admin
from .models import Example, HiddenFieldsAdmin
admin.site.register(Example, HiddenFieldsAdmin)
Now I can use it on every class with non-editable fields and so far I saw no unwanted side effects.
You can try this
#admin.register(AgentLinks)
class AgentLinksAdmin(admin.ModelAdmin):
readonly_fields = ('link', )