GAE NDB Accessing sub-instances (fields) within structured repeated list - python

I am having difficulties with accessing an instance within a structured list.
Below is my structured list:
class FavFruits(ndb.Model):
fruit = ndb.StringProperty()
score = ndb.IntegerProperty()
comment = ndb.TextProperty()
class UserProfile(ndb.Model):
uid = ndb.StringProperty(required=True)
password = ndb.StringProperty(required=True)
firstName = ndb.StringProperty(required=True)
favFruits = ndb.StructuredProperty(FavFruits, repeated=True)
I want to display score under FavFruits entity.
I tried UserProfile.favFruits.score with no luck.
I also tried UserProfile.favFruits[index].score, which worked, but now requires looping and I would like to avoid it.
Ultimately, I want to do the following logic:
if UserProfile.uid == userEntering then user enters fruit name
if UserProfile.favFruits.fruit == fruitName (user entered) then display UserProfile.favFruits.score and UserProfile.favFruits.comments for UserProfile.favFruits.fruit specified by user.
Lastly, I would like to display all the fruit/scores that user enters. Say, user entered "apple" and "orange" for fruit names, then I want to loop, for example (along this line):
for x in fruitNames
print x
print UserProfile.favFruits.score.query(UserProfile.favFruits.fruit == x)
Is this possible? Seemingly trivial task, but I cannot figure this out..
Thank you in advance!

Your requirements are contradictory. If you don't want to loop, then don't use repeated=True. But then you won't be able to store more than one for each entity. There's no possible way to have multiple things without looping or indexing.

Related

Runtime Foreign Key vs Integerfield

I have a problem. I already have two solution for my problem, but i was wondering which of those is the faster solution.
I guess that the second solution is not only more convienient- to use but also faster, but i want to be sure, so thats the reason why im asking.
My problem is i want to group multiple rows together. The group won't hold any meta data. So im only interested in runtime.
On the one hand i can use a Integer field and filter it later on when i need to get all entries that belong to the group. I guess runtime of O(n).
class SingleEntries(models.Model):
name = models.CharField(max_length=20)
group = models.IntegerField(null=True)
def find_all_group_members(id):
return SingleEntries.objects.filter(group=id)
The second solution and probably the more practicle way would be to create a foreign key to another model only using the pk there.
Then i can use the reverse relation to find all the entries that belong to the group.
class Group(models.Model):
id = models.AutoField(primary_key=True)
class SingleEntries(models.Model):
name = models.CharField(max_length=20)
group = models.ForeignKey(Group,on_delete=models.CASCADE,null=True)
def find_all_group_members(id):
return Group.objects.get(id=id).singleentries_set.all()
The first is more efficient, since this will use one query, whereas the latter will first fetch the Group, and then another one for the SingleEntries.
Indeed, if you work with:
SingleEntries.objects.filter(group=id)
this will make a simple query:
SELECT appname_singleentries.*
FROM appname_singleentries
WHERE appname_singleentries.group_id = id
It thus does not first fetch the Group into memory.
The latter will however make two queries. Indeed, it will first make a query to retrieve the Group, and then it will make a query like the one above to fetch the SingleEntries.
The two are also semantically not entirely the same: if there is no such group, then the former will return an empty QuerySet, whereas the latter will raise a Group.DoesNotExists exception.
But you can model this with:
class Group(models.Model):
pass
class SingleEntries(models.Model):
name = models.CharField(max_length=20)
group = models.ForeignKey(Group,on_delete=models.CASCADE,null=True)
def find_all_group_members(id):
return SingleEntries.objects.filter(group_id=id)
So you can use a Group model without having to retrieve the Group first.
If the groups are static in nature, that means if you don't see more groups coming to your system, you can use choices in Django.
Define choices as below
class GroupType(models.IntegerChoices):
GROUP_0 = 0, "Group 0 name"
GROUP_1 = 1, "Group 1 name"
GROUP_2 = 2, "Group 2 name"
And use it as choices field in the SingleEntries model as below
class SingleEntries(models.Model):
name = models.CharField(max_length=20)
group = models.IntegerField(choices=GroupChoices.choices, default=<set default here>)
If the groups are dynamic, meaning users can create groups whenever they want, in that case, go with your second approach of having another model for group.

How to compare user input to class object data?

total beginner here.
I'm trying to understand how to compare user input data to preset class data. Let's say class = Person, with the age, weight, and name.
And I've got two people, johnsmith = Person(50, 200, "John Smith")
tomjones = Person(40, 220, "Tom Jones")
I want to prompt the user to input the name Tom, and have check all "name" attributes for the Person class. So something like,
person = input(print("Insert the person's name: ")) entering "Tom" and then returning a list of all Toms for the user to select, or if there's only one, confirm that the user in fact did mean Tom Jones.
Should I create an array like people = [tomjones, johnsmith, (etc)], and somehow search inside that?
Please point me in the right direction on this, thanks.
Should I create an array-like people = [tomjones, johnsmith, (etc)], and somehow search inside that?
Yes.
Although ihough if you expect a very large number of "people" and frequent queries always on the same field (e.g. you're always searching for a specific person by its name) you may also want to create an index to speed up searches e.g. create a dict (possibly a WeakValueDictionary) which maps whatever your search key is to the proper person.
This is because iterating a list is cheap and efficient but going through the entire list is linear time (O(n)) so if your list is very long and you go through it a lot, it adds up. Building an index is expensive (and requires going through the entire list at least once) and there is more setup to the lookup but setup aside the lookup is constant time (O(1)).
Storing the instances of your Person class in a list would be a way of doing this. You can then loop through the list to match the names.
e.g.
class Person():
def __init__(self, age, weight, name):
self.age = age
self.weight = weight
self.name = name
all_persons = []
all_persons.append(Person(50, 200, "John Smith")) # add your Person instance to a list
all_persons.append(Person(40, 220, "Tom Jones"))
name_input = input("Insert the person's name: ")
for person in all_persons: # loop through list of all Person instances
if name_input.lower() in person.name.lower(): # lower() to handle case
# do something with person

Django filter many to many model's JSON field with the given

I have Movies and Participants model and it is like this,
class Movie(models.Model):
something something
participants = models.ManyToManyField(Participant)
class Participant(models.Model):
something something
type = models.CharField(max_length=127, null=True, blank=True)
What I would like to do is, check the Participants type field with the given list and according to it list the Movies or not.
For example, I have type_list=["Adults", "Children", "Senior"] but Movie object has 2 Participant objects and one of them is type="Adults" and the other one is type="Children"
In example I would expect not to show that Movies since it doesn't have all the required Participants type.
What have I tried so far;
movie.participants.filter(type__in=["Adults", "Children", "Senior"])
however, this returns a two participants object
movie.participants.filter(Q(type="Adults") | Q(type="Children") | Q(type="Senior")):
this one also returns the two participant object.
I also cant use the & operator.
The only idea I left with is to check the count of the participants. Query returned two but I have three participant so I can't show this movie but the problem is in here list is variable that coming from front end. So my both query and if statement should be generic and I don't know how to do both and also I am %100 sure that there should be a best practice rather than this.
I would appreciate every tiny help, thank you!
You can fetch all particiant's types using values_list with distinct and compare returned value with provided type_list by iterating over provided list:
types_from_db = movie.participants.values_list("type", flat=True).distinct()
for movie_type in ["Adults", "Children", "Senior"]:
if movie_type not in types_from_db:
return False
return True

Understanding ndb key class vs KeyProperty

I've looked through the documentation, the docs and SO questions and answers and am still struggling with understanding a small piece of this. Which should you choose and when?
This is what I've read so far (just sample):
ndb documentation
movie database structure on SO
Parent Key issues
The key class seems pretty straightforward to me. When you create an ndb entity the datastore automatically creates for you a key usually in the form of key(Kind, id) where the id is created for you .
So say you have these two models:
class Blah(ndb.Model):
last_name = ndb.StringProperty()
class Blah2(ndb.Model):
first_name = ndb.StringProperty()
blahkey = ndb.KeyProperty()
So just using the key kind and you want to make Blah1 a parent (or have several family members with the same last name)
lname = Blah(last_name = "Bonaparte")
l_key = lname.put() **OR**
l_key = lname.key.id() # spits out some long id
fname_key = l_key **OR**
fname_key = ndb.Key('Blah', lname.last_name) # which is more readable..
then:
lname = Blah2( parent=fname_key, first_name = "Napoleon")
lname.put()
lname2 = Blah2( parent=fname_key, first_name = "Lucien")
lname2.put()
So far so good (I think). Now about the KeyProperty for Blah2. Assume Blah1 is still the same.
lname3 = Blah2( first_name = "Louis", blahkey = fname_key)
lname3.put()
Is this correct ?
How to query various things
Query Last Name:
Blah.query() # all last names
Blah.query(last_name='Bonaparte') # That specific entity.
First Name:
Blah2.query()
napol = Blah2.query(first_name = "Napoleon")
bonakey = napol.key.parent().get() # returns Bonaparte's key ??
bona = bonakey.get() # I think this might be redundant
this is where I get lost. How to look for Bonaparte from first name by using either key or keyproperty. I didn't add it here and perhaps should have and that is the discussion of parents, grand parents, great grand parents since Keys keep track of ancestors/parents.
How and why would you use KeyProperty vs the inherent key class. Also imagine you had 3 sensors s1, s2, s3. Each sensor had thousands of readings but you want to keep readings associated with s1 so that you could graph say All readings for today for s1. Which would you use? KeyProperty or the key class ? I apologize if this has been answered elsewhere but I didn't see a clear example/guide about choosing which and why/how.
I think the confusion comes from using a Key. A Key is not associated with any properties inside of an entity, it is only a unique identifier to locate a single entity. It can be either a number or a string.
Fortunately, all your code looks good except for this one line:
fname_key = ndb.Key('Blah', lname.last_name) # which is more readable..
Constructing a Key takes a unique ID, which is not the same as a property. That is, it won't associate the variable lname.last_name with the property last_name. Instead, you can create your record like this:
lname = Blah(id = "Bonaparte")
lname.put()
lname_key = ndb.Key('Blah', "Bonaparte")
You are guaranteed to have only one Blah entity with that ID. In fact, if you use a string like last_name as the ID, you don't need to store it as a separate property. Think of the entity ID as an extra string property that is unique.
Next, Be careful not to assume that Blah.last_name and Blah2.first_name are unique in your queries:
lname = Blah2( parent=fname_key, first_name = "Napoleon")
lname.put()
If you do this more than once, there will be multiple entities with a first_name of Napoleon (all with the same parent key).
Continuing with your code from above:
napol = Blah2.query(first_name = "Napoleon")
bonakey = napol.key.parent().get() # returns Bonaparte's key ??
bona = bonakey.get() # I think this might be redundant
napol holds a Query, not a result. You need to call napol.fetch() to get all entities with "Napolean" (or napol.get() if you're sure there is just one entity).
bonakey is the opposite, it holds the parent entity because of the get() and not Bonaparte's key. If you left the .get() off, then bona would correctly have the parent.
Finally, your question about sensors. You may not need KeyProperty or "inherent" keys. If you have a Readings class like this:
class Readings(ndb.Model):
sensor = ndb.StringProperty()
reading = ndb.IntegerProperty()
then you can store them all in a single table without keys. (You may want to include a timestamp or other attribute.) Later, you can retrieve then with this query:
s1_readings = Readings.query(Readings.sensor == 'S1').fetch()
I'm new to NDB also, and I'm still not understanding all for now, but I think that when you create Blah2 with a parent for Napoleon, you will need the parent to query it or will not appear. For example:
napol = Blah2.query(first_name = "Napoleon")
will not get anything (and you are not using the right format for NDB), but using the parent will do:
napol = Blah2.query(ancestor=fname_key).filter(Blah2.first_name == "Napoleon").get
Don't know if this puts some light for your question.

Querying datastore for all models that contain a certain element within a listproperty

Hey, I have a model like this:
class List(db.Model):
user = db.ReferenceProperty(User)
listname = db.StringProperty()
published = db.DateTimeProperty(auto_now_add=True)
score = db.IntegerProperty(required=False)
tld = db.StringProperty(required=False)
categories = db.StringListProperty()
Where a list could have multiple categories attached to it (hence the listproperty). I want to use these categories to build category pages so I need a query I can use to fetch all lists that have a particular category within their categories attribute. I've tried a few different approaches and none seem to work. Is there a query like this this? (the following doesn't work):
select * from List where 'Philosophy' in categories
If not, I'm going to have to do something like:
lists = List.all()
for list in lists:
if 'Philosophy' in list.categories:
#add this list to the lists to display on page
But this feels like it would be either incredibly slow or break somehow...
Any ideas? Thanks!
Tom
UPDATE:
Oops, I solved it sorry for the bother! For anyone who's interested you can just use a query like this:
SELECT * FROM List where categories = 'Philosophy'
Which will match any list which has 'Philosophy' within the categories.
Oops, I solved it sorry for the bother! For anyone who's interested you can just use a query like this:
SELECT * FROM List where categories = 'Philosophy'
Which will match any list which has 'Philosophy' within the categories.

Categories