Django ManyToMany passing variables - python

I have started my Django project and I want to share some data between 2 classes.
I'm not sure if I am doing it correctly. It works but i don't want to populate in my project bad practices.
My code looks like this:
class Products(models.Model):
name = models.CharField(max_length=50)
protein = models.FloatField()
carbohydrates = models.FloatField()
fat = models.FloatField()
food_type = models.CharField(max_length=6, choices=(
("1", "Meat"),
("2", "Fruit")
)
)
class Meals(models.Model):#Child
name = models.CharField(max_length=50)
ingredient = models.ManyToManyField(Products)
def protein(self):
protein = 0
for ing in self.ingredient.all():
protein += ing.protein
return protein
#property
def carbohydrates(self):
carbohydrates = 0
for ing in self.ingredient.all():
carbohydrates += ing.carbohydrates
return carbohydrates
def diet_category(self):
diet_types = "vegan, vegeterian, Keto, Paleo, Gluten-free"
food_types = ""
for ing in self.ingredient.all():
food_types += ing.food_type
if "1" in food_types:
diet_types.replace("vegan, ", "").replace(" vegeterian,", "")
return (diet_types + " | " + food_types)
Additionally I have problem with .replace() function in python, which i want to use here to exclude some words from variable.
Summing up my questions are:
-Retrieving properties from another class is done by referring to an object of that class.
In this case Products.objects.all()
-How can I remove words from variable in models.
-Should i use #property for functions which are returning values from another class?

Replace()
The replace() function returns a copy of the string with the replaced values. It will not change the original string.
So you need to do something like
diet_types = diet_types.replace("vegan, ", "")
diet_types = diet_types.replace("vegetarian, ", "")
Fetching values
You can loop like you do or use a query to achieve the same, somthing like:
def protein(self):
return self.ingredient.aggregate(Sum('protein'))['protein__sum']
See the docs on aggregation for more details.
Properties
IMHO it is a matter of taste

Your classes can have properties dependent on other classes but make sure that you handle exception where there are no values found in other class.
So I believe using #property is not a bad idea.
Moreover, did you try using
diet_types.replace("vegan, ", " vegeterian")

Related

Check if object has relation with other in ManyToMany field and aggregate field

I have a couple models as follows:
class Student(models.Model):
name = models.CharField()
classes = models.ManyToManyField(Class, related_name="students")
class Class(models.Model):
nombre = models.CharField
What I need is a way such that, when querying students in certain class, instead of just returnig the students enrolled in that class, it returns a QuerySet of all the students, each one with an aggregated field indicating if such student is enrolled in that class, e.g.:
[{'name':'student A', 'enrolled_in_physics':True}, {'name':'student B', 'enrolled_in_physics':False}]
I think it can be achieved through F() expressions along with ExpressionWrapper, but have no idea of how implement them; additionaly, the documentation and the examples are not very noob-friendly. Any help is appreciated, thanks!.
EDIT: Ok, I think using the word "list" is not the correct one, I need a normal QuerySet, such that let me do something like this:
student_a = query[0]
student_a.name
>>>'A'
student_a.enrolled_in_physics
>>>True
UPDATED
You could try defining a method within the student class:
class Student(models.Model):
name = models.CharField()
classes = models.ManyToManyField(Class, related_name="students")
def enrolled_in_class(self,class_name):
if len(Class.objects.filter(nombre=class_name,students__in=self)) > 0:
return True
else:
return False
students = Student.objects.all()
student_a = students[0]
student_a.name
student_a.enrolled_in_class('physics')
Original Answer:
Edit: Sorry misread question, I'll try and get an answer together for what you actually wanted: A list of all students, and binary true false if they are enrolled in a specific class.
Alright, I'm reading this as a:
I need a query that returns a list of dictionaries, indicating the student, and enrollment in a class.
#Assume: class_name = 'physics'
def get_class_list(class_name):
filtered_class = Class.object.get(nombre=class_name)
student_names = Students.objects.filter(classes__contains=filtered_class).values_list('name',flat=True)
class_list = []
for name in student_names:
class_list = {}
class_list['name'] = name
enrolled_class_string = 'enrolled_in_' + class_name
class_list[enrolled_class_string] = True
return class_list
This will give you a list of dictionaries with the keys named 'name', and 'class__name'

Django - cannot retrieve just one record in multi-part filter on model with multiple relations

I can't seem to isolate a single record from this query:
subcust = OwnerCustom.objects.get(carcustom=ncset, owner=sset)
This is the error:
OwnerCustom matching query does not exist
In the actual data, there is only actually one matching record in OwnerCustom for each record in CarCustom. It's supposed to be a kind of many-to-many where there are standard differences listed in CarCustom for each Car, and each owner may maintain their own customizations (overrides) or those default OwnerCustom entries.
Note, there are many different Owner of the same Car. And of course, I'm not actually doing cars, this is a renaming from the original purpose.
Here's the relevant models:
class Car(models.Model):
car_name = models.CharField(max_length=50)
class CarCustom(models.Model):
car = models.ForeignKey(Car, models.PROTECT)
class Owner(models.Model):
car = models.ForeignKey(Car, models.PROTECT)
class OwnerCustom(models.Model):
owner = models.ForeignKey(Owner, models.PROTECT)
carcustom = models.ForeignKey(CarCustom, models.PROTECT)
name = models.CharField(max_length=50)
And the code:
car_queryset = Car.objects.filter(car_name="fancy car")
for nset in car_queryset:
owner_queryset = Owner.objects.filter(car=nset)
for sset in owner_queryset :
carcustom_queryset = CarCustom.objects.filter(car=nset)
for ncset in carcustom_queryset:
subcust = OwnerCustom.objects.get(carcustom=ncset, owner=sset)
I've tried stuff like:
subcust = OwnerCustom.objects.filter(carcustom=ncset, owner=sset).first()
Which gives me a NoneType, and then tried:
subcust = OwnerCustom.objects.filter(carcustom=ncset, owner=sset)[:1].get()
Which gives "matching query does not exist" and this:
subcust = OwnerCustom.objects.filter(carcustom=ncset, owner=sset)[0]
Gives "list index out of range"
UPDATE: I CAN get a working function by using code like this, but I would think since there is only one (guaranteed by application) matching record possible for OwnerCustom.objects.filter(carcustom=ncset, owner=sset) that I could find a better way to fetch it:
car_queryset = Car.objects.filter(car_name="fancy car")
for nset in car_queryset:
owner_queryset = Owner.objects.filter(car=nset)
for sset in owner_queryset :
carcustom_queryset = CarCustom.objects.filter(car=nset)
for ncset in carcustom_queryset:
subcust_queryset = OwnerCustom.objects.filter(carcustom=ncset, owner=sset)
for subcust in subcust_queryset :
logger.info(subcust.name)

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.

Creating a queryset which represents a union of querysets

Let's say I have the following models:
class House(models.Model):
address = models.CharField(max_length=255)
class Person(models.Model):
name = models.CharField(max_length=50)
home = models.ForeignKey(House, null=True, related_name='tenants')
class Car(models.Model):
make = models.CharField(max_length=50)
owner = models.ForeignKey(Person)
Let's say I have a need (strange one, albeit) to get:
list of people who live in a house or are named 'John'
list of cars of the above people
I would like to have two functions:
get_tenants_or_johns(house)
get_cars_of_tenants_or_johns(house)
I could define them as:
from django.db.models.query_utils import Q
def get_cars_of_tenants_or_johns(house):
is_john = Q(owner__in=Person.objects.filter(name='John'))
is_tenant = Q(owner__in=house.tenants.all())
return Car.filter(is_john | is_tenant)
def get_tenants_or_johns(house):
johns = Person.objects.filter(name='John')
tenants = house.tenants.all()
return set(johns) | set(tenants)
The problem is that the logic is repeated in the above examples. If I could get get_tenants_or_johns(house) to return a queryset I could define get_cars_of_tenants_or_johns(house) as:
def get_cars_of_tenants_or_johns(house):
return Car.objects.filter(owner__in=get_tenants_or_johns(house))
In order to do that, get_tenants_or_johns(house) would need to return a union of querysets, without turning them into other collections.
I cannot figure out how to implement get_tenants_or_johns(house) so that it would return a queryset containing a SQL UNION. Is there a way to do that? If not, is there an alternate way to achieve what I am trying to do?
The | operator on two querysets will return a new queryset representing a union.
The function will need to change to (got rid of set() wrappers):
def get_tenants_or_johns(house):
johns = Person.objects.filter(name='John')
tenants = house.tenants.all()
return johns | tenants
and everything will work exactly like needed.
You mention users who live in a house, but have no mention of your User model.
I think you really need to take a long look at the structure of your application - there are probably much easier ways to accomplish your goal.
But to answer your question let's set up three helper functions. Since, as I mentioned above, you haven't outlined what you want to do with the User class - I've assumed that the house that will be passed to these functions is an address:
helpers.py
def get_johns(house):
is_john = Person.objects.filter(name='John')
return is_john
def get_cars_of_tenants(house):
cars = Car.objects.filter(owner__home__address=house)
return cars
def get_tenants(house):
tenants = Person.objects.filter(home__address=house)
return tenants
Now you could create a view for each of your combination queries:
views.py:
import helpers.py
from itertools import chain
def get_cars_of_tenants_or_johns(request, house):
results = list(chain(get_cars_of_tenants(house), get_johns(house)))
return render_to_response('cars_or_johns.html', {"results": results,})
def get_tenants_or_johns(request, house):
results = list(chain(get_tenants(house), get_johns(house)))
return render_to_response('tenants_or_johns.html', {"results": results,})
And this can go on for all of the various combinations. What is returned is results which is a list of all of the matches that you can iterate over.

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