Following this answer I wanted to add a linked column to my admin page,
class AnswerAdmin(admin.ModelAdmin):
list_display = ('__str__', 'link_to_question', 'time_created', 'time_updated', 'created_by', 'down_vote', 'up_vote')
def link_to_question(self, obj):
link = urlresolvers.reverse("admin:QnA_question_change",
args=[obj.question.id]) # model name has to be lowercase
text = obj.question.__str__
str = format_html("{}", text)
return mark_safe(u'%s' % (link, str))
class Meta:
model = Answer
but what I get in return is this:
<bound method Entry.__str__ of <Question: This is a question about technology?>>
I only want the "This is a question ..." part shown in my admin.
Sidenote:
when I use something like obj.question.text instead of a function it works smoothly.
It isn't clear why you are using format_html then passing the result to mark_safe. You should be able to do it in one step with format_html. This has the advantage of escaping text, in case a use has inserted malicious content.
link = urlresolvers.reverse(...)
text = obj.question
link_str = format_html('{}', link, text)
To call the __str__ method you need to call it with obj.question.__str__(). However, it's more pythonic to call str(obj.question) rather than obj.question.__str__(). In this case, I don't think you need to use str() at all, since you are using format_html.
just set the allow_tags = True property of the method.
class AnswerAdmin(admin.ModelAdmin):
list_display = ('__str__', 'link_to_question', 'time_created', 'time_updated', 'created_by', 'down_vote', 'up_vote')
def link_to_question(self, obj):
link = urlresolvers.reverse("admin:QnA_question_change",
args=[obj.question.id]) # model name has to be lowercase
return u'{1}'.format(link, obj.question)
link_to_question.short_description = u'Link'
link_to_question.allow_tags = True
Related
I have a simple search in my Django project. I want to search through documents using their type and part of factory info in addition to search by name.
Here is my models.py:
class Docs(models.Model):
Date = models.DateField(default=date.today)
Name = models.CharField(max_length=50)
Type = models.ForeignKey(DocTypes)
Part = models.ForeignKey(Parts)
Link = models.FileField(upload_to='Docs/%Y/%m/%d')
class Parts(models.Model):
Name = models.CharField(max_length=50)
def __str__(self):
return str(self.Name)
class DocTypes(models.Model):
Type = models.CharField(max_length=50)
def __str__(self):
return str(self.Type)
My forms.py:
class DocsSearchForm(ModelForm):
class Meta:
model = Docs
fields = [ 'Name', 'Type', 'Part']
And this is part of my views.py, if no search was done then all documents are given
def showdocs(request):
if request.method == 'POST':
form = DocsSearchForm(request.POST)
documents = Docs.objects.filter(Name__contains=request.POST['Name']|
Type==request.POST['Type']|
Part==request.POST['Part'])
else:
form = DocsSearchForm()
documents = Docs.objects.all()
return render(
request,
'showdocs.html',
{'documents': documents, 'form':form}
So, the problem is the following: if I try to use a search then I have
NameError at /showdocs
name 'Type' is not defined.
POST values are:Part '1', Name 'Example', Type '1'.
If I delete
Type==request.POST['Type']|
Part==request.POST['Part']
then search by name works well. So I have a guess that problem is about searching by foreign key values, but have no ideas more. Will appreciate any help.
Try replacing the line with this
Docs.objects.filter(Name__contains=request.POST['Name'],
Type=request.POST['Type'],
Part=request.POST['Part']
)
It seems you have misunderstood the syntax. I don't know why you are trying to use | operator here.
That's not how Django filters work. You can't | them because they are not actually expressions, just keyword arguments. In this case, correct syntax would be:
Docs.objects.filter(
Name__contains=request.POST['Name'],
Type_Type=request.POST['Type'],
Part_Name=request.POST['Part'],
)`
I'm doing a simple filter -
filters.py
class TblserversFilter(django_filters.FilterSet):
name = django_filters.CharFilter(name="servername", lookup_type="exact")
class Meta:
model = Tblservers
fields = ['servername']
What I would like to do, if possible, is to have two lookup_types associated with the field. Specifically I want exact AND contains and then somehow replace the operator depending on the filter.
name=serverabc would be an exact search and name~abc will be a fuzzy search.
You could do a method_filter and then prefix your filter queries with different symbols for exact and icontains and other filters that you want at the client side.
Since code is better than a thousand words:
exact_prefix = '#'
icontains_prefix = '~'
class TblserversFilter(django_filters.FilterSet):
name = django_filters.MethodFilter(
action=name_filter)
def name_filter(self, value):
if value:
value_prefix = value[0]
if value_prefix == exact_prefix:
return self.filter(name=value)
elif value_prefix == icontains_prefix:
return self.filter(name__icontains=value)
# this can continue for all the types of filters you want
else:
return self.filter(name=value)
else:
return self.filter(name=value)
class Meta:
model = Tblservers
fields = ['servername']
EDIT:
In django-filter 1.0 MethodFilter was replaced with Filter's method argument. So solution rewritten for v1.0 would be following (not tested):
exact_prefix = '#'
icontains_prefix = '~'
class TblserversFilter(django_filters.FilterSet):
name = django_filters.CharFilter(
method='name_filter')
def name_filter(self, qs, name, value):
if value:
value_prefix = value[0]
if value_prefix == exact_prefix:
return qs.filter(name=value)
elif value_prefix == icontains_prefix:
return qs.filter(name__icontains=value)
# this can continue for all the types of filters you want
else:
return qs.filter(name=value)
else:
return qs.filter(name=value)
class Meta:
model = Tblservers
fields = ['servername']
First my apologies for the shameless library self-plug.
At some point I was trying to do something similar in django-filters however the solution was much more complex then anticipated. I ended up creating my own library for doing filtering in Django which natively supports the exact functionality you are looking for - django-url-filter. Its API is very similar to django-filters:
from django import forms
from url_filter.filter import Filter
from url_filter.filtersets import ModelFilterSet
class TblserversFilter(FilterSet):
name = Filter(form_field=forms.CharField(max_length=15), lookups=['exact', 'contains'])
class Meta(object):
model = Tblservers
fields = ['name', 'servername']
Note that the URL will look a bit different though:
?name=foo # exact
?name__exact=foo
?name__contains=foo
Also you will need to manually call the filter set in order to filter the queryset:
fs = TblserversFilter(data=query, queryset=...)
filtered_qs = fs.filter()
Syntax of the URL parameters is very similar to Django ORM.
You can look at the docs for more examples. Hopefully it might be of use.
Apart from one example in the docs, I can't find any documentation on how exactly django chooses the name with which one can access the child object from the parent object. In their example, they do the following:
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __unicode__(self):
return u"%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place, primary_key=True)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
def __unicode__(self):
return u"%s the restaurant" % self.place.name
# Create a couple of Places.
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
>>> p2.save()
# Create a Restaurant. Pass the ID of the "parent" object as this object's ID.
>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()
# A Restaurant can access its place.
>>> r.place
<Place: Demon Dogs the place>
# A Place can access its restaurant, if available.
>>> p1.restaurant
So in their example, they simply call p1.restaurant without explicitly defining that name. Django assumes the name starts with lowercase. What happens if the object name has more than one word, like FancyRestaurant?
Side note: I'm trying to extend the User object in this way. Might that be the problem?
If you define a custom related_name then it will use that, otherwise it will lowercase the entire model name (in your example .fancyrestaurant). See the else block in django.db.models.related code:
def get_accessor_name(self):
# This method encapsulates the logic that decides what name to give an
# accessor descriptor that retrieves related many-to-one or
# many-to-many objects. It uses the lower-cased object_name + "_set",
# but this can be overridden with the "related_name" option.
if self.field.rel.multiple:
# If this is a symmetrical m2m relation on self, there is no reverse accessor.
if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model:
return None
return self.field.rel.related_name or (self.opts.object_name.lower() + '_set')
else:
return self.field.rel.related_name or (self.opts.object_name.lower())
And here's how the OneToOneField calls it:
class OneToOneField(ForeignKey):
... snip ...
def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(),
SingleRelatedObjectDescriptor(related))
The opts.object_name (referenced in the django.db.models.related.get_accessor_name) defaults to cls.__name__.
As for
Side note: I'm trying to extend the
User object in this way. Might that be
the problem?
No it won't, the User model is just a regular django model. Just watch out for related_name collisions.
I want to create a new type of field for django models that is basically a ListOfStrings. So in your model code you would have the following:
models.py:
from django.db import models
class ListOfStringsField(???):
???
class myDjangoModelClass():
myName = models.CharField(max_length=64)
myFriends = ListOfStringsField() #
other.py:
myclass = myDjangoModelClass()
myclass.myName = "bob"
myclass.myFriends = ["me", "myself", "and I"]
myclass.save()
id = myclass.id
loadedmyclass = myDjangoModelClass.objects.filter(id__exact=id)
myFriendsList = loadedclass.myFriends
# myFriendsList is a list and should equal ["me", "myself", "and I"]
How would you go about writing this field type, with the following stipulations?
We don't want to do create a field which just crams all the strings together and separates them with a token in one field like this. It is a good solution in some cases, but we want to keep the string data normalized so tools other than django can query the data.
The field should automatically create any secondary tables needed to store the string data.
The secondary table should ideally have only one copy of each unique string. This is optional, but would be nice to have.
Looking in the Django code it looks like I would want to do something similar to what ForeignKey is doing, but the documentation is sparse.
This leads to the following questions:
Can this be done?
Has it been done (and if so where)?
Is there any documentation on Django about how to extend and override their model classes, specifically their relationship classes? I have not seen a lot of documentation on that aspect of their code, but there is this.
This is comes from this question.
There's some very good documentation on creating custom fields here.
However, I think you're overthinking this. It sounds like you actually just want a standard foreign key, but with the additional ability to retrieve all the elements as a single list. So the easiest thing would be to just use a ForeignKey, and define a get_myfield_as_list method on the model:
class Friends(model.Model):
name = models.CharField(max_length=100)
my_items = models.ForeignKey(MyModel)
class MyModel(models.Model):
...
def get_my_friends_as_list(self):
return ', '.join(self.friends_set.values_list('name', flat=True))
Now calling get_my_friends_as_list() on an instance of MyModel will return you a list of strings, as required.
What you have described sounds to me really similar to the tags.
So, why not using django tagging?
It works like a charm, you can install it independently from your application and its API is quite easy to use.
I also think you're going about this the wrong way. Trying to make a Django field create an ancillary database table is almost certainly the wrong approach. It would be very difficult to do, and would likely confuse third party developers if you are trying to make your solution generally useful.
If you're trying to store a denormalized blob of data in a single column, I'd take an approach similar to the one you linked to, serializing the Python data structure and storing it in a TextField. If you want tools other than Django to be able to operate on the data then you can serialize to JSON (or some other format that has wide language support):
from django.db import models
from django.utils import simplejson
class JSONDataField(models.TextField):
__metaclass__ = models.SubfieldBase
def to_python(self, value):
if value is None:
return None
if not isinstance(value, basestring):
return value
return simplejson.loads(value)
def get_db_prep_save(self, value):
if value is None:
return None
return simplejson.dumps(value)
If you just want a django Manager-like descriptor that lets you operate on a list of strings associated with a model then you can manually create a join table and use a descriptor to manage the relationship. It's not exactly what you need, but this code should get you started.
Thanks for all those that answered. Even if I didn't use your answer directly the examples and links got me going in the right direction.
I am not sure if this is production ready, but it appears to be working in all my tests so far.
class ListValueDescriptor(object):
def __init__(self, lvd_parent, lvd_model_name, lvd_value_type, lvd_unique, **kwargs):
"""
This descriptor object acts like a django field, but it will accept
a list of values, instead a single value.
For example:
# define our model
class Person(models.Model):
name = models.CharField(max_length=120)
friends = ListValueDescriptor("Person", "Friend", "CharField", True, max_length=120)
# Later in the code we can do this
p = Person("John")
p.save() # we have to have an id
p.friends = ["Jerry", "Jimmy", "Jamail"]
...
p = Person.objects.get(name="John")
friends = p.friends
# and now friends is a list.
lvd_parent - The name of our parent class
lvd_model_name - The name of our new model
lvd_value_type - The value type of the value in our new model
This has to be the name of one of the valid django
model field types such as 'CharField', 'FloatField',
or a valid custom field name.
lvd_unique - Set this to true if you want the values in the list to
be unique in the table they are stored in. For
example if you are storing a list of strings and
the strings are always "foo", "bar", and "baz", your
data table would only have those three strings listed in
it in the database.
kwargs - These are passed to the value field.
"""
self.related_set_name = lvd_model_name.lower() + "_set"
self.model_name = lvd_model_name
self.parent = lvd_parent
self.unique = lvd_unique
# only set this to true if they have not already set it.
# this helps speed up the searchs when unique is true.
kwargs['db_index'] = kwargs.get('db_index', True)
filter = ["lvd_parent", "lvd_model_name", "lvd_value_type", "lvd_unique"]
evalStr = """class %s (models.Model):\n""" % (self.model_name)
evalStr += """ value = models.%s(""" % (lvd_value_type)
evalStr += self._params_from_kwargs(filter, **kwargs)
evalStr += ")\n"
if self.unique:
evalStr += """ parent = models.ManyToManyField('%s')\n""" % (self.parent)
else:
evalStr += """ parent = models.ForeignKey('%s')\n""" % (self.parent)
evalStr += "\n"
evalStr += """self.innerClass = %s\n""" % (self.model_name)
print evalStr
exec (evalStr) # build the inner class
def __get__(self, instance, owner):
value_set = instance.__getattribute__(self.related_set_name)
l = []
for x in value_set.all():
l.append(x.value)
return l
def __set__(self, instance, values):
value_set = instance.__getattribute__(self.related_set_name)
for x in values:
value_set.add(self._get_or_create_value(x))
def __delete__(self, instance):
pass # I should probably try and do something here.
def _get_or_create_value(self, x):
if self.unique:
# Try and find an existing value
try:
return self.innerClass.objects.get(value=x)
except django.core.exceptions.ObjectDoesNotExist:
pass
v = self.innerClass(value=x)
v.save() # we have to save to create the id.
return v
def _params_from_kwargs(self, filter, **kwargs):
"""Given a dictionary of arguments, build a string which
represents it as a parameter list, and filter out any
keywords in filter."""
params = ""
for key in kwargs:
if key not in filter:
value = kwargs[key]
params += "%s=%s, " % (key, value.__repr__())
return params[:-2] # chop off the last ', '
class Person(models.Model):
name = models.CharField(max_length=120)
friends = ListValueDescriptor("Person", "Friend", "CharField", True, max_length=120)
Ultimately I think this would still be better if it were pushed deeper into the django code and worked more like the ManyToManyField or the ForeignKey.
I think what you want is a custom model field.
I have a ChoiceField, now how do I get the label when I need it?
class ContactForm(forms.Form):
reason = forms.ChoiceField(choices=[("feature", "A feature"),
("order", "An order")],
widget=forms.RadioSelect)
form.cleaned_data["reason"] only gives me the feature or order values or so.
See the docs on Model.get_FOO_display(). So, should be something like :
ContactForm.get_reason_display()
In a template, use like this:
{{ OBJNAME.get_FIELDNAME_display }}
This may help:
reason = form.cleaned_data['reason']
reason = dict(form.fields['reason'].choices)[reason]
This the easiest way to do this: Model instance reference: Model.get_FOO_display()
You can use this function which will return the display name: ObjectName.get_FieldName_display()
Replace ObjectName with your class name and FieldName with the field of which you need to fetch the display name of.
If the form instance is bound, you can use
chosen_label = form.instance.get_FOO_display()
Here is a way I came up with. There may be an easier way. I tested it using python manage.py shell:
>>> cf = ContactForm({'reason': 'feature'})
>>> cf.is_valid()
True
>>> cf.fields['reason'].choices
[('feature', 'A feature')]
>>> for val in cf.fields['reason'].choices:
... if val[0] == cf.cleaned_data['reason']:
... print val[1]
... break
...
A feature
Note: This probably isn't very Pythonic, but it demonstrates where the data you need can be found.
You can have your form like this:
#forms.py
CHOICES = [('feature', "A feature"), ("order", "An order")]
class ContactForm(forms.Form):
reason = forms.ChoiceField(choices=CHOICES,
widget=forms.RadioSelect)
Then this would give you what you want:
reason = dict(CHOICES)[form.cleaned_data["reason"]]
OK. I know this is very old post, but reading it helped me a lot. And I think I have something to add.
The crux of the matter here is that the the model method.
ObjectName.get_FieldName_display()
does not work for forms.
If you have a form, that is not based on a model and that form has a choice field, how do you get the display value of a given choice.
Here is some code that might help you.
You can use this code to get the display value of a choice field from a posted form.
display_of_choice = dict(dateform.fields['fieldnane'].choices)[int(request.POST.get('fieldname'))]
the 'int' is there on the basis the choice selection was a integer. If the choice index was a string then you just remove the int(...)
Im using #Andrés Torres Marroquín way, and I want share my implementation.
GOOD_CATEGORY_CHOICES = (
('paper', 'this is paper'),
('glass', 'this is glass'),
...
)
class Good(models.Model):
...
good_category = models.CharField(max_length=255, null=True, blank=False)
....
class GoodForm(ModelForm):
class Meta:
model = Good
...
good_category = forms.ChoiceField(required=True, choices=GOOD_CATEGORY_CHOICES)
...
def clean_good_category(self):
value = self.cleaned_data.get('good_category')
return dict(self.fields['good_category'].choices)[value]
And the result is this is paper instead of paper.
Hope this help
I think maybe #webjunkie is right.
If you're reading from the form from a POST then you would do
def contact_view(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
contact = form.save()
contact.reason = form.cleaned_data['reason']
contact.save()
confirm that Ardi's and Paul's response are best for forms and not models. Generalizing Ardi's to any parameter:
class AnyForm(forms.Form):
def get_field_name_display(self, field_name):
return dict(self.fields[field_name].choices[self.cleaned_data[field_name]]
Or put this method in a separate class, and sub-class it in your form
class ChoiceFieldDisplayMixin:
def get_field_name_display(self, field_name):
return dict(self.fields[field_name].choices[self.cleaned_data[field_name]]
class AnyCustomForm(forms.Form, ChoiceFieldDisplayMixin):
choice_field_form = forms.ChoiceField(choices=[...])
Now call the same method for any Choice Field:
form_instance = AnyCustomForm()
form_instance.is_valid()
form_instance.get_field_name_display('choice_field_form')