Choose one no empty of two fields for __str__() - python

In my model I have two fields for the title, one for language. I want name the post with the title in the user language if there is, else in the other language.
My model.py:
class Post(models.Model):
title_it = Model.CharField(_('title'), max_length=64, blank=True)
title_en = Model.CharField(_('title'), max_length=64, blank=True)
def __str__(self):
name_traslated={'title_it': self.title_it, 'title_en': self.title_en}
name_verbose=_('title_it')
name=name_traslated[name_verbose]
if name=='':
name=name_traslated['title_it']
if name=='':
name=name_traslated['title_en']
if name=='':
name=ugettext('No Title')
There is some faster way to do so? For example what about this:
def __str__(self):
name_traslated={'title_it': self.title_it, 'title_en': self.title_en}
name_verbose=_('title_it')
name=self.title_it
if name_traslated[name_verbose]: #!=''
name=name_traslated[name_verbose]
elif name==''
name=ugettext('No Title')
return name

You can use the python version of a ternary operation to shorten the clauses.
class Post:
title_it = 'ciao mondo'
title_en = 'hello world'
def __str__(self):
name_translated = {'title_it': self.title_it, 'title_en': self.title_en}
name_verbose = 'title_it'
out = name_translated[name_verbose] if name_translated[name_verbose] else name_translated['title_en']
return out if out else 'No Title'

Depending on your frequency of use and desired extension to more languages, you might want to maintain a preference list. In your three-way case above, I'd generalize this with:
pref_list = [
name,
name_translated[user_language],
name_translated[default_language]
]
Now, you simply pull out the first one that has text within it. You can do this by checking string contents or length. The entire operation can be wrapped up in a single expression, if you like.

Related

Storing a function call in a Django model

Currently I am storing a series of objects as a dictionary of dictionaries, and within this I store calls to functions defined outside of the dictionary. These functions are specific to the objects, and cannot be generalised. In the dictionary I can refer to the function directly eg: 'some_property': function_name, and when I call that later on dictionary['some_property'](arg_1, arg_2)and the function gets called. I am looking migrate this dictionary of dictionaries to a django model, but I cannot see how I can replicate this functionality from a model.
What I currently have:
dictionaries.py
def year_camel_month(filename, **kwargs):
month = kwargs['month'].title()
return filename.format(str(kwargs['year']), month)
def year_month(filename, **kwargs):
month = kwargs['month']
return filename.format(str(kwargs['year']), month.lower())
data_source_families = {
'dataset_1': {
'source_url': 'https://example.org/url/subfolder',
'slug': 'slug_that_changes_predictably_over_time{}-{}',
'slug_treatment': year_camel_month
},
'dataset_2': {
'source_url': 'https://example2.org/url/subfolder',
'slug': 'slug_that_changes_predictably_over_time{}-{}',
'slug_treatment': year_month
},
}
Which then gets called when combined with a user-defined time frame later on:
get_data.py
from .dictionaries import data_source_families
slug = data_source_families[selected_dataset]['slug']
processed_slug = data_source_families[selected_dataset]['slug_treatment'](slug, some_kwargs)
url = data_source_families[selected_dataset]['source_url'] + processed_slug
And this is working fine. I am looking to develop functionality to improve consistency (and make these data available to another programme) by creating a django model that replicates this, something like this:
models.py
def year_camel_month(filename, **kwargs):
month = kwargs['month'].title()
return filename.format(str(kwargs['year']), month)
def year_month(filename, **kwargs):
month = kwargs['month']
return filename.format(str(kwargs['year']), month.lower())
class DataSourceFamilies(models.Model):
name = models.CharField(max_length=200, unique=True)
source_url = models.CharField(max_length=300, blank=False)
slug = models.CharField(max_length=200, blank=False)
--> slug_treatment = models._____(choices=list_of_functions) <--
def __str___(self):
return self.name.name
Does something like this exist? How would I go about doing this?
You cannot store functions (Python functions I mean) in a SQL database, indeed. But you can store any text value, and you can have a dict of 'key:func' in your model, ie:
class DataSourceFamilies(models.Model):
name = models.CharField(max_length=200, unique=True)
source_url = models.CharField(max_length=300, blank=False)
slug = models.CharField(max_length=200, blank=False)
SLUG_TREATEMENTS = [
# key, label, function
('year_camel_month', "Year, Camel month", year_camel_month),
('year_month': "Year month", year_month),
]
SLUG_TREATEMENTS_ACTIONS = {
k: func
for k, label, func in SLUG_TREATEMENTS
}
SLUG_TREATEMENTS_CHOICES = [
(k, label)
for k, label, func in SLUG_TREATEMENTS
]
slug_treatment = models.CharField(
max_length=50 # let's have a little headroom,
choices=SLUG_TREATMENT_CHOICES
)
def get_slug_treatment_func(self):
return self.SLUG_TREATEMENTS_ACTIONS[self.slug_treatment]
One thing you could do is to use a CharField and then eval it. However, using eval is usually a huge security risk. Any python code that enters it will be executed, and you do not want anything like that in a web application.
Another option is to have a lookup system. You could, say, have a CharField with choices that corresponds to a dictionary like so:
models.py
...
slug_treatment = models.CharField(max_length=100, choices=function_choices)
...
And then:
get_data.py
function_lookup = {
"year_month": year_month,
"year_camel_month": year_camel_month
}
processed_slug = function_lookup[data_source.slug_treatment](slug, some_kwargs)
Sry its a bit confusing to me, but based on what i understood, maybe you can declare all function in your class and use slug_treament as parameter to which function will be called when you need.
Lets draw it a bit
YEAR_CAMEL_MONTH=1
YEAR_MONTH=2
SLUG_TREATEMENTS_CHOICES = [
(YEAR_CAMEL_MONTH: 'year_camel_month'),
(YEAR_MONTH: 'year_month'),
]
class DataSourceFamilies(models.Model):
...
slug = models.CharField(max_length=200, blank=False)
slug_treatment = models.IntegerField(choices=SLUG_TREATMENT_CHOICES)
def year_camel_month(self):
... # Your logic
return formated_slug
def year_month(self):
... # Your logic
return formated_slug
def save(self *args **kwargs):
if self.slug_treatment == YEAR_CAMEL_MONTH:
self.slug = self.year_camel_month()
elif self.slug_treatment == YEAR_MONTH:
self.slug = self.year_month()
super(DataSourceFamilies, self).save(*args, **kwargs)
Or you can use it as prorperty method instead of persisted data (so the slug will be evaluated everytime your call your queryset, so its is dinamic instead of persisted" Obs.: Property methods works like columns from your database, but its not persisted, its like CAST in database
class DataSourceFamilies(models.Model):
...
slug_treatment = models.IntegerField(choices=SLUG_TREATMENT_CHOICES)
#property
def slug(self):
if self.slug_treatment == YEAR_CAMEL_MONTH:
return slug = self.year_camel_month()
elif self.slug_treatment == YEAR_MONTH:
return slug = self.year_month()
https://docs.djangoproject.com/en/2.0/topics/db/models/
Obs.: If you trying to get code from text and evaluate it in python i guess its possible, but is highly unsafe, and i do not recommend it

NameError in Django simple search

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

How to sort a Django QuerySet by (field, custom function, field)

I am looking for getting a QuerySet that is sorted by field1, function, field2.
The model:
class Task(models.Model):
issue_id = models.CharField(max_length=20, unique=True)
title = models.CharField(max_length=100)
priority_id = models.IntegerField(blank=True, null=True)
created_date = models.DateTimeField(auto_now_add=True)
def due_date(self):
...
return ageing
I'm looking for something like:
taskList = Task.objects.all().order_by('priority_id', ***duedate***, 'title')
Obviously, you can't sort a queryset by custom function. Any advise?
Since the actual sorting happens in the database, which does not speak Python, you cannot use a Python function for ordering. You will need to implement your due date logic in an SQL expression, as an Queryset.extra(select={...}) calculated field, something along the lines of:
due_date_expr = '(implementation of your logic in SQL)'
taskList = Task.objects.all().extra(select={'due_date': due_date_expr}).order_by('priority_id', 'due_date', 'title')
If your logic is too complicated, you might need to implement it as a stored procedure in your database.
Alternatively, if your data set is very small (say, tens to a few hundred records), you can fetch the entire result set in a list and sort it post-factum:
taskList = list(Task.objects.all())
taskList.sort(cmp=comparison_function) // or .sort(key=key_function)
The answer by #lanzz, even though seems correct, didn't work for me but this answer from another thread did the magic for me:
https://stackoverflow.com/a/37648265/6420686
from django.db.models import Case, When
ids = [list of ids]
preserved = Case(*[When(id=pk, then=pos) for pos, pk in enumerate(ids)])
filtered_users = User.objects \
.filter(id__in=ids) \
.order_by(preserved)
You can use sort in Python if the queryset is not too large:
ordered = sorted(Task.objects.all(), key=lambda o: (o.priority_id, o.due_date(), o.title))

django - how to sort objects alphabetically by first letter of name field

I have a model which has the fields word and definition. model of dictionary.
in db, i have for example these objects:
word definition
-------------------------
Banana Fruit
Apple also Fruit
Coffee drink
I want to make a query which gives me, sorting by the first letter of word, this:
Apple - also Fruit
Banana - Fruit
Coffee -drink
this is my model:
class Wiki(models.Model):
word = models.TextField()
definition = models.TextField()
I want to make it in views, not in template. how is this possible in django?
Given the model...
class Wiki(models.Model):
word = models.TextField()
definition = models.TextField()
...the code...
my_words = Wiki.objects.order_by('word')
...should return the records in the correct order.
However, you won't be able to create an index on the word field if the type is TextField, so sorting by word will take a long time if there are a lot of rows in your table.
I'd suggest changing it to...
class Wiki(models.Model):
word = models.CharField(max_length=255, unique=True)
definition = models.TextField()
...which will not only create an index on the word column, but also ensure you can't define the same word twice.
Since you tagged your question Django, I will answer how to do it using Django entities.
First, define your entity like:
class FruitWords(models.Model):
word = models.StringField()
definition = models.StringField()
def __str__(self):
return "%s - %s" % (self.word, self.definition)
To get the list:
for fruit in FruitWords.all_objects.order_by("word"):
print str(fruit)
If you are using class based ListView
class WikiListView(ListView):
model = Wiki
template_name = # Path to your html code. Example: 'appName/htmlFileName.html
def get_context_data(self, *args, **kwargs):
wiki_list = Wiki.objects.order_by('word')
context = super(WikiListView, self).get_context_data(*args, **kwargs)
context["wiki_list"] = wiki_list
return context
if you are using a simple view
def WikiView(request):
wiki_list = Wiki.objects.order_by('word')
return render(request, """HTML File""", {'wiki_list': wiki_list})
for example, I have an Article object that have a title field.
articles = list(sorted(articles, key=lambda obj:obj.title))
the issue you may run into is that you must require to return a QuerySet in class method occasions like get_queryset, the solution is stopping using a class based view and switch to a function view.

django query based on dynamic property()

I was wondering if there was a way to use Django's filter() on query sets using a dynamically generated python property using property(). I have first_name and last_name of every user, and I want to filter based on their concatenated name first_name last_name. (The reason behind this is that when I do autocomplete I search to see if the query matches first name, last name, or part of the concatenation. I want John S to match John Smith, for example.
I created a property of name:
def _get_name(self):
return self.first_name + " " + self.last_name
name = property(_get_name)
This way I can call user.name to get the concatenated name.
However, if I try to do User.objects.filter(name__istartswith=query) I get the error Cannot resolve keyword 'name' into field.
Any ideas on how to do this? Do I have to create another field in the database to store the full name?
The accepted answer is not entirely true.
For many cases, you can override get() in the model manager to pop dynamic properties from the keyword arguments, then add the actual attributes you want to query against into the kwargs keyword arguments dictionary. Be sure to return a super so any regular get() calls return the expected result.
I'm only pasting my own solution, but for the __startswith and other conditional queries you could add some logic to split the double-underscore and handle appropriately.
Here was my work-around to allow querying by a dynamic property:
class BorrowerManager(models.Manager):
def get(self, *args, **kwargs):
full_name = kwargs.pop('full_name', None)
# Override #1) Query by dynamic property 'full_name'
if full_name:
names = full_name_to_dict(full_name)
kwargs = dict(kwargs.items() + names.items())
return super(BorrowerManager, self).get(*args, **kwargs)
In models.py:
class Borrower(models.Model):
objects = BorrowerManager()
first_name = models.CharField(null=False, max_length=30)
middle_name = models.CharField(null=True, max_length=30)
last_name = models.CharField(null=False, max_length=30)
created = models.DateField(auto_now_add=True)
In utils.py (for the sake of context):
def full_name_to_dict(full_name):
ret = dict()
values = full_name.split(' ')
if len(values) == 1:
raise NotImplementedError("Not enough names to unpack from full_name")
elif len(values) == 2:
ret['first_name'] = values[0]
ret['middle_name'] = None
ret['last_name'] = values[1]
return ret
elif len(values) >= 3:
ret['first_name'] = values[0]
ret['middle_name'] = values[1:len(values)-1]
ret['last_name'] = values[len(values)-1]
return ret
raise NotImplementedError("Error unpacking full_name to first, middle, last names")
filter() operates on the database level (it actually writes SQL), so it won't be possible to use it for any queries based on your python code (dynamic property in your question).
This is an answer put together from many other answers in this department : )
I had a similar problem and was looking for solution. Taking for granted that a search engine would be the best option (e.g. django-haystack with Elasticsearch), that's how I would implement some code for your needs using only the Django ORM (you can replace icontains with istartswith):
from django.db.models import Value
from django.db.models.functions import Concat
queryset = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name')
return queryset.filter(full_name__icontains=value)
In my case I didn't know whether the user would insert 'first_name last_name' or viceversa, so I used the following code.
from django.db.models import Q, Value
from django.db.models.functions import Concat
queryset = User.objects.annotate(first_last=Concat('first_name', Value(' '), 'last_name'), last_first=Concat('last_name', Value(' '), 'first_name'))
return queryset.filter(Q(first_last__icontains=value) | Q(last_first__icontains=value))
With Django <1.8, you would probably need to resort to extra with the SQL CONCAT function, something like the following:
queryset.extra(where=['UPPER(CONCAT("auth_user"."last_name", \' \', "auth_user"."first_name")) LIKE UPPER(%s) OR UPPER(CONCAT("auth_user"."first_name", \' \', "auth_user"."last_name")) LIKE UPPER(%s)'], params=['%'+value+'%', '%'+value+'%'])
Think it's not possible in django to filter on properties that does not present as a database filed, but what you can do to make cool autocomplete search is something like this:
if ' ' in query:
query = query.split()
search_results = list(chain(User.objects.filter(first_name__icontains=query[0],last_name__icontains=query[1]),
User.objects.filter(first_name__icontains=query[1],last_name__icontains=query[0])))
else:
search_results = User.objects.filter(Q(first_name__icontains=query)| Q(last_name__icontains=query))
This code gives the user of your system a flexibility to start typing either first name or last name and the user will be thankful to you for allowing this.

Categories

Resources