How to use only form validation of ModelForm? - django

How to use only form validation of ModelForm?
Please see my code!
models.py
from django.db import models
class IPAddr(models.Model):
ip = models.GenericIPAddressField(unique=True,)
class myModel(models.Model):
ip = models.ForeignKey(IPAddr)
forms.py
from django.forms import ModelForm
from django import forms
from app.models import * # This is thing above models.py
class myModelForm(ModelForm):
ip = forms.GenericIPAddressField()
class Meta:
model = myModel
I want below logic.
user input ip(to ip field) -> validation -> (form.is_valid is True) goodIP = IPAddr.objects.get_or_create(user_input_ip) -> myModel.objects.create(ip=goodIP)
But validation is always fail...
Because (for example) user input is '1.2.3.4'. As you know, '1.2.3.4' is valid IP Address.
But form.is_valid is False. Because of '1.2.3.4' is not IPAddr instance.
So I can't use logic that I want.
Let's see ModelForm document.
The first time you call is_valid() or access the errors attribute of a ModelForm triggers form validation as well as model validation.
I want validation about only form validation...
...
Do I must use Forms not ModelForm...?
While using ModelForm, can I use my logic that I want?

class myModelForm(forms.ModelForm):
ip = forms.GenericIPAddressField()
class Meta:
model = myModel
def clean(self):
cleaned_data = super(ContactForm, self).clean()
ip = cleaned_data.get("ip")
//do your login here
return cleaned_data

Related

Add a button in a Django ModelForm

I have created a simple form in which the user is specifying the name of a (Bloomberg)Ticker. In a second field the user is entering an address of a webpage. Of course, she/he can manually correct it but I would like to offer him/her a button such that the app suggests the name of the page based on the content of first Ticker field, e.g. the user enters "CARMPAT FP Equity" and then the app suggests correctly
https://www.bloomberg.com/quote/CARMPAT:FP
# forms.py
from django.forms import ModelForm, ValidationError
from pybloomberg.bloomberg import valid_ticker
from .models import Symbol
class SymbolAdminForm(ModelForm):
# Have a button next to address field...
class Meta:
model = Symbol
fields = '__all__'
def clean(self):
cleaned_data = self.cleaned_data
ticker = cleaned_data.get('bloomberg_symbol')
return cleaned_data
def react(self):
# extract the content for the bloomberg_symbol field
address = ...
# manipulate the content for the address field
self.... = address
If you want the suggestion to be applied automatically after the form is posted, you can move your logic to the clean() method and you will need to allow the address field to be empty.
def clean(self):
cleaned_data = self.cleaned_data
ticker = cleaned_data.get('bloomberg_symbol')
cleaned_data['address'] = ticker_name_to_address()
return cleaned_data
If you want the suggestion to be displayed to the users when they enter the name, you will have no choice but to use JavaScript.

After update to DRF 3.0 and Django 1.8, Serialiser method invalid

I am not sure whether it is related or not. But before I update to DRF 3.0 and Django 1.8, my following code is working properly.
class DialogueSerializer(serializers.ModelSerializer):
sound_url = serializers.SerializerMethodField('get_sound_url')
class Meta:
model = Dialogue
fields = ('id','english','myanmar', 'sound_url')
def get_sound_url(self, dialogue):
if not dialogue.sound:
return None
request = self.context.get('request')
sound_url = dialogue.sound.url
return request.build_absolute_uri(sound_url)
Some people also said like this.
https://github.com/lightweightdjango/examples/issues/2
Now, when I run, I got
It is redundant to specify get_sound_url on SerializerMethodField
'sound_url' in serializer 'DialogueSerializer', because it is the same
as the default method name. Remove the method_name argument.
How shall I do?
Since your field is sound_url and your method get_field_name (get_sound_url), you don't have to give to SerializerMethodField the method name.
As you can see in the example from DRF documentation, there is no need to precise method_name in this case.
from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
days_since_joined = serializers.SerializerMethodField()
class Meta:
model = User
def get_days_since_joined(self, obj):
return (now() - obj.date_joined).days
So, the result is :
class DialogueSerializer(serializers.ModelSerializer):
sound_url = serializers.SerializerMethodField() # no more `method_name` passed
class Meta:
model = Dialogue
fields = ('id','english','myanmar', 'sound_url')
def get_sound_url(self, dialogue):
if not dialogue.sound:
return None
request = self.context.get('request')
sound_url = dialogue.sound.url
return request.build_absolute_uri(sound_url)

How to tie django error validation to a specific ForeignKey field, not the entire model

class Business(models.Model):
is_distributor = models.BooleanField()
class Invoice(models.Model):
from_business = models.ForeignKey(Business)
to_business = models.ForeignKey(Business)
To be valid, Invoice.from_business.is_distributor must be True. I can do this in clean() but this error would be tie to the entire model, not the specific from_business field.
I also don't think validator can be hook to ForeignKey field.
You can easily get to the instance of the foreign key field and validate a property using the clean method:
from django import forms
from your_app.models import Invoice
class InvoiceForm(forms.ModelForm):
def clean(self):
cleaned_data = self.cleaned_data()
business = cleaned_data.get('business')
if not business.is_distributor:
self._errors['business'] = self.error_class(
['Business must be a distributor.'])
del cleaned_data['business']
return cleaned_data

ModelForm is_valid() always return false during unit testing

I have a simple Django code.
there is my model and form in models.py file:
from django.db import models
from django.forms import ModelForm
class Supplier(models.Model):
name = models.CharField(max_length=55)
comment = models.TextField(blank=True)
class SupplierForm(ModelForm):
class Meta:
model = Supplier
and there is my test.py:
from django.test import TestCase
from mysite.myapp.models import Supplier, SupplierForm
class SupplierTest(TestCase):
def test_supplier(self):
supplier = Supplier(name="SomeSupplier")
supplier_form = SupplierForm(instance = supplier)
self.assertEquals(supplier_form.is_valid(), True)
When I start test through manage.py, is_valid() always returns False, but I expect True.
What the reasons for fail is_valid() in this case ?
I use Django 1.3.
All forms constructed without data are "invalid" because they have nothing to validate :-) You need to supply valid input to form's constuctor:
supplier_form = SupplierForm({'name': 'NewSupplier'}, instance=supplier)

Django admin: How to display a field that is marked as editable=False' in the model?

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', )