I have a custom type in dexterity (schema driven) without title or description fields.
class IAnimal(model.Schema):
name = schema.TextLine(title=_(u"Name"),required=True,)
specie = schema.Choice(title=_(u"Specie"),vocabulary=animalSpecies)
weight = schema.TextLine(title=_(u"Weight"))
(etc)
I really don't need the title field on my model, but when I create some content, on folder listing is displaying:
— by admin — last modified Oct 17, 2015 02:27 PM
I created this product with mr.bob and didn't override any forms yet. This can be accomplished by override any forms, a custom behavior (like plone.app.content.interfaces.INameFromTitle) or what?
I just want the "name" field as "title" without changing "name" for "title", even that I must have to hide the "title" field in the model.
Looking at some old archetypes products, it was a method like:
def at_post_create_script(self):
title = self.name
plone_utils = getToolByName(self, 'plone_utils', None)
new_id = plone_utils.normalizeString(title)
self.setTitle(new_id)
I didn't get why you can't simply provide a "title" field named "Name" (easiest way), however you can also override the title attribute and Title method of the base class.
If you used mr.bob you probably don't have a base class you can customize.
Check your type definition XML: in the klass property you probably have plone.dexterity.content.Item. Change it to a new dexterity base class you must add to you product (example: https://github.com/collective/wildcard.media/blob/84b82994dc424fe40b92b1c9af8d48edb558a78d/wildcard/media/content.py#L6)
When you have the base class you can add the Title method and a title attribute, something like:
class MyType(Item):
implements(IMyType)
def Title(self):
return self.name
#property
def title(self)
return self.name
Related
I'm implementing a tagging system. Currently, the models look like this:
class Tag(models.Model):
label = models.CharField(max_length=MAX_TAG_LENGTH)
class TagManager(models.Model):
tags = models.ManyToManyField(Tag, related_name="referrers")
def associate_tag(self, tag_label: str):
. . .
And I have a custom field that cuts its input on commas so the user can enter tags as a comma-separated list:
class TagsField(forms.CharField):
def to_python(self, value):
if not value:
return []
return [tag.strip() for tag in value.split(',')]
Finally, I have the model and form where these are used:
class Thing(models.Model):
tags = models.OneToOneField(TagManager, on_delete=models.SET_NULL, null=True)
class ThingForm(forms.ModelForm):
tags = TagsField(widget=forms.TextInput(attrs={"placeholder": "Tags", "required": False}))
class Meta:
model = Thing
fields = ["tags"]
Problem
My issue is that if I populate and validate the form:
form = ThingForm(data={"tags": ["One", "Two"]})
form.is_valid()
I get errors:
{'tags': ["“["One", "Two"]” value must be an integer."]}
Which I'm guessing is because it's trying to place the stringified list in the OneToOneField, which isn't going to work.
What I really need to do is after validating the field, I need to iterate the results of to_python, and call thing_instance.tags.associate_tag on each of the validated tag strings.
Is there a "hook" method on forms that will allow me to do this cleanly? I've read over the docs and Form source and can't find any obvious candidates.
I realized as I was writing this that it's a clean_* method that I need. This didn't strike me as "cleaning" on first brush, so I ignored it.
The solution was to add a clean_tags method to the ThingForm class:
def clean_tags(self):
tags = self.cleaned_data["tags"]
for tag in tags:
self.instance.tags.associate_tag(tag)
return self.instance.tags.pk
It associates the cleaned tags, and then returns the PK of the TagManager they were added to.
I was going through code of https://github.com/hit9/CURD.py/blob/master/CURD.py which is a simple orm that performs normal curd operations .. and i could not understood part of code which goes like this(on line number 616):
.....#smthing #..
for name, attr in cls.__dict__.iteritems():
if isinstance(attr, Field):
attr.describe(name, cls)
fields[name] = attr
what does attr.describe(attr, Field) do ? I googled it out but found nothing.
It's not a Python language feature, it's a method on that library. You can see the definition here:
https://github.com/hit9/CURD.py/blob/master/CURD.py#L251
class Field(Leaf):
"""
Field object.
Field examples: User.name, User.age ..
"""
def __init__(self, is_primarykey=False, is_foreignkey=False):
self.is_primarykey = is_primarykey
self.is_foreignkey = is_foreignkey
# describe model's attr
def describe(self, name, model):
self.name = name
self.model = model
# fullname e.g. : User.id 's fullname is "user.id"
self.fullname = self.model.table_name + "." + self.name
# describe the attribute, reload its access control of writing, reading
setattr(model, name, FieldDescriptor(self))
setattr sets an attribute on an object. So if I call describe("field_name", myObject), it will set myObject.field_name to the description of the model. Or something like that.
That is not a python standard thing.
The loop iterates through the names and values of a class, and the attributes of that class that are instances of a Field type are added to a dictionary.
Field is not part of python standard library, you should search that project for the Field class.
Given the following models:
class Module(models.Model):
pass
class Content(models.Model):
module = models.ForeignKey(Module, related_name='contents')
class Blog(Module):
pass
class Post(Content):
pass
I would like to be able to get all the "post" objects owned by blog doing something like:
b = Blog.objects.get(pk=1)
b.posts.all()
However, I haven't figured out a good way of doing this. I can't use b.contents.all() as I need Post instances and not Content instances. I won't ever have a root content object, every content object is going to be subclassed, but I can't use abstract classes as I want a central table with all my content in it and then there will be content_blog etc tables for all the unique inherited pieces of content.
I also tried doing this
class Content(models.Model):
module = models.ForeignKey(Module, related_name='%(class)')
but that failed miserably as far as I could tell.
The simplest way might add a method to Blog model to return a Post queryset, like this:
class Blog(Module):
def _get_posts(self):
return Post.objects.filter(module=self)
posts = property(_get_posts)
The problem is you have to add method for every sub-model. The related_name seems only works for abstract base class.
This solution comes to my mind:
# ...
class Blog(Module):
#property
def posts(self):
return self.contents
class Post(Content):
pass
This way, doing blog.posts is the same as doing blog.contents:
>>> blog = Blog.objects.get(pk=1)
>>> blog.posts.all()
# [ ... ]
I'd like to set custom name to a FileField object in my admin form so that it's html name attribute will not be equal to my instance field name.
class MyAdminForm(forms.ModelForm):
file_field = forms.FileField()
def __init__(self, *args, **kwargs):
if kwargs.has_key('instance'):
instance = kwargs['instance']
self.initial['image_file'] = instance.file_field
With this code I get <input type="file" name="file_field" /> and what I want to do is set it's name attribute to something else.
EDIT:
I accepted the answer below, but I have another question. Is it possible to construct variable number of FileField objects? I mean - what if I'd like to have 4 of those now, but under some circumstances only one? Will I have to declare all of those as a class fields, like file_field1, file_field2 and so on, or is it possible to add them to a dictionary, like that: { 'file_field1: FileField(), 'file_field2' : FileField() } - I actually tried it and got an error...
The name attribute in the HTML is the same as the name in the form definition so if you don't want it to be file_field then don't call it file_field.
class MyAdminForm(forms.ModelForm):
new_field = forms.FileField()
# Rest of the form goes here
I'm trying to pass on additional information to fields of a Django form to be displayed in a template. I tried to override the constructor and add another property to the field like this:
self.fields['field_name'].foo = 'bar'
but in the template this:
{{ form.field_name.foo }}
didn't print anything. Does anyone know how to add additional information to a field without rewriting/inheriting the forms field classes?
According to django.forms.forms, the __getitem__() method of a Form creates something called a BoundField out of the Field before returning it, thus stripping it of whatever changes you made. If you really want to insert more functionality into that, override that method to do stuff to the bound field before returning it:
class MyForm(forms.Form):
def __getitem__(self, name):
boundfield = super(forms.Form,self).__getitem__(name)
boundfield.foo = "bar"
return boundfield
Then, "bar" will appear for all fields in that form. You can also make a function and call that instead, to make it more than just a hard-coded string.
While it's more standard to add more fields, or to add properties to the form itself, if you have a whole new class of info that every field needs to contain, this may do it for you.
Another way to get the same thing is to edit an attribute of the field, then access it via the BoundField's "field" attribute:
class MyForm(forms.Form):
def __init__(self, *args, **kwargs)
super(forms.Form, self).__init__(*args, **kwargs)
self.fields['field_name'].foo = "bar"
Then, to access foo in a template:
{{ form.field_name.field.foo }}