I am new to django and I know there are many stack overflow questions and answers related with this topic, but none of the solutions seems to work for me. I am trying to override django's default error messages.
I tried these to name a few of the solutions I tried.
class MyForm(forms.ModelForm):
class Meta:
error_messages = {
'first_name': {
'required': _("First name is required."),
},
}
Also tried this
class MyRequest(models.Model):
first_name = models.CharField(
max_length=254,
blank=False,
error_messages={
'blank': 'my required msg..',
}
)
Is there any thing I need to do on the template side?
Override the field like this
`first_name = forms.CharField(error_messages={'required':_("First name is required.")})`
NB: if the error is for the condition required, adding a message for blank or null won't cut it. also in your form's Meta, don't forget to add your model.
I suggest the following:
(In general the required property is assigned to form field by default)
class MyForm(forms.ModelForm):
# we want to add or overwrite this field
first_name = forms.CharField(label="First Name", required=True,
help_text="Required: Enter your first name")
class Meta:
# If the form is related to some model, add the model name here
model = ModelNameGoesHere
Related
Updated
I changed my simplified question into a real example.
I've created a working post response of data from the model using ModelSerialzer, which I call from a post method in a view class. I would like to add additional data to the response. This is the pertinent code from my CBV:
def post(self, request, format=None):
user_profile = UserProfiles.objects.get(user=request.user.id)
service_id = user_profile.service_id
rec_filter = Recommendations.objects.values_list('resource')
if service_id > 0:
service_name = Services.objects.get(pk=service_id)
programs = Programs.objects.filter(services=service_id)
resources_filtered = Resources.objects.filter(program__in=programs).exclude(id__in=rec_filter)
else:
service_name = 'All Services'
resources_filtered = Resources.objects.exclude(id__in=rec_filter)
serializer = ResourceSerializer(resources_filtered, many=True)
#serializer.data["service"] = service_name
return Response(serializer.data)
The commented out line was my attempt to add data base on a similar post here. I get a 500 response in my API call. What is the correct way to do it? The response data is JSON if that's necessary to mention.
This is the ModelSerializer:
class ResourceSerializer(serializers.ModelSerializer):
organization = OrganizationSerializer(read_only=True)
program = ProgramSerializer(read_only=True)
class Meta:
model = Resources
fields = [
'organization',
'program',
'link',
'contact',
'general_contact',
'eligibility',
'service_detail'
]
Test of the answer
Heres the updated code based on the answer with a correction to fix and error:
class ResourceSerializer(serializers.ModelSerializer):
organization = OrganizationSerializer(read_only=True)
program = ProgramSerializer(read_only=True)
service = serializers.SerializerMethodField()
def get_service(self, obj):
return "All Services"
class Meta:
model = Resources
fields = [
'organization',
'program',
'link',
'contact',
'general_contact',
'eligibility',
'service_detail',
'service'
]
The problem with this approach is that the value "All Services" is repeated in every row serialized. It's only needed once. I'd also like to keep the data transmitted minimized.
The problem with the original attempt is that serializer.data is immutable. It's necessary to make a copy and add to it.
serializer = ResourceSerializer(resources_filtered, many=True)
augmented_serializer_data = list(serializer.data)
augmented_serializer_data.append({'service': 'All Services'})
return Response(augmented_serializer_data)
This answer is based on one given by #andre-machado in this question.
This code here is an example to coincide with the other answer given.
You can do it in serializer itself. Define the new field required and add it in fields. Mark all the fields in serializer from resource model.
class ResourceSerializer(serializers.ModelSerializer):
service = serializers.SerializerMethodField()
def get_service(self):
return "All Services"
class Meta :
model = Resources
fields = ('service') #Mark all the fields required here from resource model
You can do it from the serilaizer. In this case i was adding the field isOpen to the response and this is how i did it .timeDifference is the name of the function that was to generate data for the extra field . I hope it helps
class ShopSearchSerializer(serializers.ModelSerializer):
isOpen = serializers.SerializerMethodField('timeDifference')
def timeDifference(self,*args):
requestTime = datetime.now()
return requestTime
class Meta:
model = Shop
fields =['name','city','street','house','opening_time','closing_time','isOpen']
I have a SQLAlchemy model with custom type ChoiceType which comes from sqlalchemy_utils library.
class Recipient(Base, BaseMixin):
first_name = Column(String())
last_name = Column(String())
social_network = Column(ChoiceType(SOCIAL_NETWOKRS))
Where SOCIAL_NETWOKRS are SOCIAL_NETWOKRS = [
('vk', 'Vkontakte'),
('fb', 'Facebook'),
('youtube', 'Youtube'),
]
I got next error when going into admin panel for edit my model:
NotImplementedError: Not able to derive a colander type from sqlalchemy type: ChoiceType(length=255) Please explicitly provide a colander `typ` for the "social_network" Column.
How can I get around the restriction with saving autogeneration of the administrative panel?
I move from sqlalchemy_utils and add direct validation from a Colander.
Next snippet works as expected:
class Account(BaseMixin, Base):
social_network = Column(String(), info={'colanderalchemy': {
'typ': colander.String(),
'widget': deform.widget.SelectWidget(values=SOCIAL_NETWOKRS),
}})
Apologies for the awful title.
I'm setting up a website, using Flask and SQLAlchemy. I'd like a list of tags available for all content types. I'm using sqlite3 for my development database.
After inputting data using the html form, only the tag is not being saved to the db. I'm not sure where the weak point(s?) lies. I can't tell if I've got something wrong conceptually about how SQLAlchemy handles inheritance, passing arguments to a subclass and/or the many to many relationship. I'd really appreciate any clarity on the subject or recommendations about how to improve the model.
Here's the code:
I have an association table for the Many-to-Many relationship between the tags and Content:
tagging_association = Table('tagging', Model.metadata,
Column('content_id', Integer, ForeignKey('content.id')),
Column('tag_id', Integer, ForeignKey('tags.id'))
)
I've set up a Content class:
class Content(Model):
'''
The base class for all content types.
'''
__tablename__ = 'content'
id = Column(Integer, primary_key=True)
tag = relationship('Tag', secondary='tagging', backref='content')
type = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'content',
'polymorphic_on':type
}
All content types are subclasses of Content, using SQLAlchemy's Joined Table Inheritance:
class Entry(Content):
'''The database model for blog-like entries on the homepage.'''
__tablename__ = 'entries'
id = Column(Integer, ForeignKey('content.id'), primary_key=True)
title = Column(String(200))
body = Column(String)
__mapper_args__ = {
'polymorphic_identity':'entries',
}
# Want to pass a single tag first, just to get it to work. Is this how would I do that?
def __init__(self, title, body, *args, **kwargs):
super(Entry, self).__init__(*args, **kwargs)
self.title = title
self.body = body
And the Tag class:
class Tag(Model):
'''Tag database model.'''
__tablename__ = 'tags'
id = Column(Integer, primary_key=True)
tag = Column(String(30), nullable=False, unique=True)
def __init__(self, tag):
self.tag = tag
Here's my WTForms class:
class EntryForm(Form):
title = TextField('Title', validators=[Required()])
body = TextAreaField('Body', validators=[Required()])
tags = TextField('Tags')
submit = SubmitField('Submit')
Here's where I take the form data and add it to the db:
#mod.route('/add_entry/', methods=['GET', 'POST'])
#requires_admin
def add_entry():
form = EntryForm()
if form.validate():
entry = Entry(form.title(), form.body(), form.tags())
form.populate_obj(entry)
db_session.add(entry)
db_session.commit()
return redirect(url_for('general.index'))
return render_template('general/add_entry.html', form=form)
If what you showed above is the actual code, you can see that your relationship is named tag (tag = relationship(...), but in the EntryForm your TextField property is called tags. So your tag relationship is never set and therefore never saved. What is set to the field tags is just ignored. I assume you just need to rename the Content.tag to Content.tags.
Above should answer the question why it is not saved, but if you just rename the field, this will not solve your problem. You need to write code that handles your Tags properly:
when Tag text is assigned to a Content, then you need:
look if the Tag with this tag already exists.
if it does, load it.
if it does not, create it
append the found/created tag to the Content.tags
See the answer to Inserting data in Many to Many relationship in SQLAlchemy for similar problem to give you an idea what to do and how it could be done.
Imagine, I have News models with many text fields
class News(models.Model):
title = models.CharField(max_length=255)
subtitle = models.CharField(max_length=255, blank=True)
lead = models.TextField(max_length=4096)
content = models.TextField()
...
last_visited = models.DateTimeField()
Every time my News object outputs, I update last_visited field:
news.last_visited = datetime.datetime.now()
news.save()
This code makes Django override all model fields:
UPDATE news SET title='...', subtitle='...', last_visited = '...' WHERE id = '...';
Instead of just one:
UPDATE news SET last_visited = '...' WHERE id = '...';
I worried how bad it is and is it worth of thinking about.
Django documentation offers queryset update but it looks not very elegant:
def upd(obj, **kwargs):
obj.__class__._default_manager.filter(pk=obj.pk).update(**kwargs)
upd(news, last_visited=datetime.datetime.now())
I use mysql backend.
Using update but with a cleaner approach:
class News(models.Model):
def update_visited(self):
News.objects.filter(pk=self.pk).update(
last_visited=datetime.datetime.now())
I think using queryset update is good. It removes the possibility that you overwrite changes to other fields by accident.
I know you're worried that it looks inelegant, but you only have to use it once in your upd function, then use upd in your views.
Supposing you want to use this on more than one model (guessing this because you pass obj to your upd function) it would probably make sense to have some base class that implements the last_visited field and your News class inherits from this class... Then you could do the update just on your base class.... Another possibilty would be putting the last_visited information into a seperate model and referencing the News model either through a ForeignKey or a GenericForeignKey (in the case you want to keep a 'history' for different models).
I defined some WTForms forms in an application that uses SQLALchemy to manage database operations.
For example, a form for managing Categories:
class CategoryForm(Form):
name = TextField(u'name', [validators.Required()])
And here's the corresponding SQLAlchemy model:
class Category(Base):
__tablename__= 'category'
id = Column(Integer, primary_key=True)
name = Column(Unicode(255))
def __repr__(self):
return '<Category %i>'% self.id
def __unicode__(self):
return self.name
I would like to add a unique constraint on the form validation (not on the model itself).
Reading the WTForms documentation, I found a way to do it with a simple class:
class Unique(object):
""" validator that checks field uniqueness """
def __init__(self, model, field, message=None):
self.model = model
self.field = field
if not message:
message = u'this element already exists'
self.message = message
def __call__(self, form, field):
check = self.model.query.filter(self.field == field.data).first()
if check:
raise ValidationError(self.message)
Now I can add that validator to the CategoryForm like this:
name = TextField(u'name', [validators.Required(), Unique(Category, Category.name)])
This check works great when the user tries to add a category that already exists \o/
BUT it won't work when the user tries to update an existing category (without changing the name attribute).
When you want to update an existing category : you'll instantiate the form with the category attribute to edit:
def category_update(category_id):
""" update the given category """
category = Category.query.get(category_id)
form = CategoryForm(request.form, category)
The main problem is I don't know how to access the existing category object in the validator which would let me exclude the edited object from the query.
Is there a way to do it? Thanks.
In the validation phase, you will have access to all the fields. So the trick here is to pass in the primary key into your edit form, e.g.
class CategoryEditForm(CategoryForm):
id = IntegerField(widget=HiddenInput())
Then, in the Unique validator, change the if-condition to:
check = self.model.query.filter(self.field == field.data).first()
if 'id' in form:
id = form.id.data
else:
id = None
if check and (id is None or id != check.id):
Although this is not a direct answer I am adding it because this question is flirting with being an XY Problem. WTForms primary job is to validate that the content of a form submission. While a decent case could be made that verifying that a field's uniqueness could be considered the responsibility of the form validator, a better case could be made that this is the responsibility of the storage engine.
In cases where I have be presented with this problem I have treated uniqueness as an optimistic case, allowed it to pass form submission and fail on a database constraint. I then catch the failure and add the error to the form.
The advantages are several. First it greatly simplifies your WTForms code because you do not have to write complex validation schemes. Secondly, it could improve your application's performance. This is because you do not have to dispatch a SELECT before you attempt to INSERT effectively doubling your database traffic.
The unique validator needs to use the new and the old data to compare first before checking if the data is unique.
class Unique(object):
...
def __call__(self, form, field):
if field.object_data == field.data:
return
check = DBSession.query(model).filter(field == data).first()
if check:
raise ValidationError(self.message)
Additionally, you may want to squash nulls too. Depending on if your truly unique or unique but allow nulls.
I use WTForms 1.0.5 and SQLAlchemy 0.9.1.
Declaration
from wtforms.validators import ValidationError
class Unique(object):
def __init__(self, model=None, pk="id", get_session=None, message=None,ignoreif=None):
self.pk = pk
self.model = model
self.message = message
self.get_session = get_session
self.ignoreif = ignoreif
if not self.ignoreif:
self.ignoreif = lambda field: not field.data
#property
def query(self):
self._check_for_session(self.model)
if self.get_session:
return self.get_session().query(self.model)
elif hasattr(self.model, 'query'):
return getattr(self.model, 'query')
else:
raise Exception(
'Validator requires either get_session or Flask-SQLAlchemy'
' styled query parameter'
)
def _check_for_session(self, model):
if not hasattr(model, 'query') and not self.get_session:
raise Exception('Could not obtain SQLAlchemy session.')
def __call__(self, form, field):
if self.ignoreif(field):
return True
query = self.query
query = query.filter(getattr(self.model,field.id)== form[field.id].data)
if form[self.pk].data:
query = query.filter(getattr(self.model,self.pk)!=form[self.pk].data)
obj = query.first()
if obj:
if self.message is None:
self.message = field.gettext(u'Already exists.')
raise ValidationError(self.message)
To use it
class ProductForm(Form):
id = HiddenField()
code = TextField("Code",validators=[DataRequired()],render_kw={"required": "required"})
name = TextField("Name",validators=[DataRequired()],render_kw={"required": "required"})
barcode = TextField("Barcode",
validators=[Unique(model= Product, get_session=lambda : db)],
render_kw={})
Looks like what you are looking for can easily be achieved with ModelForm which is built to handle forms that are strongly coupled with models (the category model in your case).
To use it:
...
from wtforms_components import Unique
from wtforms_alchemy import ModelForm
class CategoryForm(ModelForm):
name = TextField(u'name', [validators.Required(), Unique(Category, Category.name)])
It will verify unique values while considering the current value in the model. You can use the original Unique validator with it.
This worked for me, simple and easy:
Make sure that every time when a new row created in DB it must have unique name in colomn_name_in_db otherwise it will not work.
class SomeForm(FlaskForm):
id = IntegerField(widget=HiddenInput())
fieldname = StringField('Field name', validators=[DataRequired()])
...
def validate_fieldname(self, fieldname):
names_in_db = dict(Model.query.with_entities(Model.id,
Model.colomn_name_in_db).filter_by(some_filtes_if_needed).all())
if fieldname.data in names_in_db.values() and names_in_db[int(self.id)] != fieldname.data:
raise ValidationError('Name must be unique')