Django Model ValueError - database

I'm accessing emails in my email server, taking the body of each email and then applying regular expressions to find the data necessary to populate my Django model.
This all works fine except for one field, which is linked as a foreign key to another model field. Despite the value in my email being the same as the one in listed in my other model, it fails....
The error:
ValueError: Cannot assign "'Humanities'": "Subject.faculty" must be a "Faculty" instance.
For example, say each school subject has to be part of a faculty. When populating the database via a form, for the Subject's faculty field I drop down the menu to a list of faculty values/instances as there is a foreign key relationship defined in my model i.e. for the faculty field I can choose from Humanities, Art, Design Technology etc.
But when I find the value 'Humanities' in my email and try to add it to the database model, I get the error above.
Anyone shed any light on this? Am I being stupid or is it more than a ValueError as to me, the values are the same in both cases
Thank you
More code as requested:
class Faculty(models.Model):
name = models.CharField(primary_key=True, max_length=50)
leader = models.CharField(max_length=50)
email = models.EmailField()
mailing_list = models.ManyToManyField("Contact", null=True)
class Subject(models.Model):
name = models.CharField(max_length=50)
faculty = models.ForeignKey(Faculty, to_field="name")
faculty_head = models.CharField(max_length=50)

It sounds like you are trying to assign a string "Humantities" to a ForeignKey relationship. This doesn't make sense. You need to either find or create the actual Faculty object with the name "Humanities" and assign it to the Subject. Something like this in your view (depending on how your form is set up):
if form.is_valid():
faculty_str = form.cleaned_data['faculty']
(faculty, was_created) = Faculty.objects.get_or_create(name=faculty_str, ...)
# It's hard to tell if you are using a ModelForm or just a normal Form. Anyway, assume we already have access to the Subject object
subject.faculty = faculty
subject.save()
get_or_create()

Your value is 'Humanities' perhaps you mean to search for Humanities (without quotes).

You need to create a Faculty instance first.
faculty = Faculty(name='', leader='', email='')
faculty.save()
subject.faculty = faculty
subject.save()

Related

SQLAlchemy: foreignKeys from multiple Tables (Many-to-Many)

I'm using flask-sqlalchemy orm in my flask app which is about smarthome sensors and actors (for the sake of simplicity let's call them Nodes.
Now I want to store an Event which is bound to Nodes in order to check their state and other or same Nodes which should be set with a given value if the state of the first ones have reached a threshold.
Additionally the states could be checked or set from/for Groups or Scenes. So I have three diffrent foreignkeys to check and another three to set. All of them could be more than one per type and multiple types per Event.
Here is an example code with the db.Models and pseudocode what I expect to get stored in an Event:
db = SQLAlchemy()
class Node(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(20))
# columns snipped out
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(20))
# columns snipped out
class Scene(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(20))
# columns snipped out
class Event(db.Model):
id = db.Column(db.Integer, primary_key=True)
# The following columns may be in a intermediate table
# but I have no clue how to design that under these conditions
constraints = # list of foreignkeys from diffrent tables (Node/Group/Scene)
# with threshold per key
target = # list of foreignkeys from diffrent tables (Node/Group/Scene)
# with target values per key
In the end I want to be able to check if any of my Events are true to set the bound Node/Group/Scene accordingly.
It may be a database design problem (and not sqlalchemy) but I want to make use of the advantages of sqla orm here.
Inspired by this and that answer I tried to dig deeper, but other questions on SO were about more specific problems or one-to-many relationships.
Any hints or design tips are much appreciated. Thanks!
I ended up with a trade-off between usage and lines of code. My first thought here was to save as much code as I can (DRY) and defining as less tables as possible.
As SQLAlchemy itself points out in one of their examples the "generic foreign key" is just supported because it was often requested, not because it is a good solution. With that less db functionallaty is used and instead the application has to take care about key constraints.
On the other hand they said, having more tables in your database does not affected db performance.
So I tried some approaches and find a good one that fits to my usecase. Instead of a "normal" intermediate table for many-to-many relationships I use another SQLAlchemy class which has two one-to-many relations on both sides to connect two tables.
class Event(db.Model):
id = db.Column(db.Integer, primary_key=True)
noodles = db.relationship('NoodleEvent', back_populates='events')
# columns snipped out
def get_as_dict(self):
return {
"id": self.id,
"nodes": [n.get_as_dict() for n in self.nodes]
}
class Node(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(20))
events = db.relationship('NodeEvent', back_populates='node')
# columns snipped out
class NodeEvent(db.Model):
ev_id = db.Column('ev_id', db.Integer, db.ForeignKey('event.id'), primary_key=True)
n_id = db.Column('n_id', db.Integer, db.ForeignKey('node.id'), primary_key=True)
value = db.Column('value', db.String(200), nullable=False)
compare = db.Column('compare', db.String(20), nullable=True)
node = db.relationship('Node', back_populates="events")
events = db.relationship('Event', back_populates="nodes")
def get_as_dict(self):
return {
"trigger_value": self.value,
"actual_value": self.node.status,
"compare": self.compare
}
The trade-off is that I have to define a new class everytime I bind a new table on that relationship. But with the "generic foreign key" approach I also would have to check from where the ForeignKey is comming from. Same work in the end of the day.
With my get_as_dict() function I have a very handy access to the related data.

Convert three modelsto one single query django query

This are my model with some of the fields:
class Advertisers(models.Model):
account_manager_id = models.ForeignKey(AccountManagers, on_delete=models.CASCADE,null=True, db_column='account_manager_id',related_name="advertisers")
class AdvertiserUsers(models.Model):
user_id = models.OneToOneField('Users', on_delete=models.CASCADE,null=True,db_column='user_id', related_name='advertiser_users')
advertiser_id = models.ForeignKey('Advertisers', on_delete=models.CASCADE,null=True,db_column='advertiser_id', related_name='advertiser_users')
class Users(models.Model):
email = models.CharField(unique=True, max_length=100)
I want Id's, user ids and email of all advertisers.
Id's of all user:-
advertiser_ids = advertisers.objects.all() # can get id from here
find user_ids of advertiser_ids:
user_ids = AdvertiserUsers.objects.filter(advertiser_id__in=advertiser_ids) # can get user_id from here
find id and email using this query:
user_ids = Users.objects.filter(id__in=user_ids) # can get email from here
How to make it shorter like directly querying from Advertisers i will be able to get Users models email.
Thankyou in advance
You can filter with:
Users.objects.filter(advertiser_users__advertiser_id__isnull=False).distinct()
The .distinct() [Django-doc] will prevent returning the same Users multiple times.
You can annotate the User objects with the Advertisers primary key, etc:
from django.db.models import F
Users.objects.filter(advertiser_users__advertiser_id__isnull=False).annotate(
account_manager_id=F('advertiser_users__advertiser_id__account_manager_id'),
advertiser_id=F('advertiser_users__advertiser_id')
)
The Users objects that arise from this have a .email attribute (and the other attributes that belong to a Users object), together with a .account_manager_id and an .advertiser_id. That being said, this is probably not a good idea: the way you have modeled this right now, is that a Users object can relate to multiple Advertisers objects, so it makes not much sense to add these together.
You can for each user access the related Advertisers with:
myusers = Users.objects.filter(
advertiser_users__advertiser_id__isnull=False
).prefetch_related(
'advertiser_users',
'advertiser_users__advertiser_id'
).distinct()
for user in myusers:
print(f'{user.email}')
for advuser in user.advertiser_users.all():
print(f' {advuser.advertiser_user.pk}')
Note: normally a Django model is given a singular name, so User instead of Users.
Note: Normally one does not add a suffix _id to a ForeignKey field, since Django
will automatically add a "twin" field with an _id suffix. Therefore it should
be account_manager_id, instead of account_manager.
Advertisers.objects.all().values_list('id','account_manager_id','advertiser_users__user_id',advertiser_users__user_id__email)

Maintain uniqueness of a property in the NDB database

An NDB model contains two properties: email and password. How to avoid adding to the database two records with the same email? NDB doesn't have UNIQUE option for a property, like relational databases do.
Checking that new email is not in the database before adding—won't satisfy me, because two parallel processes can both simultaneously do the checking and each add the same email.
I'm not sure that transactions can help here, I am under this impression after reading some of the manuals. Maybe the synchronous transactions? Does it mean one at a time?
Create the key of the entity by email, then use get_or_insert to check if exists.
Also read about keys , entities. and models
#ADD
key_a = ndb.Key(Person, email);
person = Person(key=key_a)
person.put()
#Insert unique
a = Person.get_or_insert(email)
or if you want to just check
#ADD
key_a = ndb.Key(Person, email);
person = Person(key=key_a)
person.put()
#Check if it's added
new_key_a =ndb.Key(Person, email);
a = new_key_a.get()
if a is not None:
return
Take care. Changing email will be really difficult (need to create new entry and copy all entries to new parent).
For that thing maybe you need to store the email, in another entity and have the User be the parent of that.
Another way is to use Transactions and check the email property. Transaction's work in the way: First that commits is the First that wins. A concept which means that if 2 users check for email only the first (lucky) one will succeed, thus your data will be consistent.
Maybe you are looking for the webapp2-authentication module, that can handle this for you. It can be imported like this import webapp2_extras.appengine.auth.models. Look here for a complete example.
I also ran into this problem, and the solution above didn't solve my problem:
making it a key was unacceptable in my case (i need the property to be changeable in the future)
using transactions on the email property doesn't work AFAIK (you can't do queries on non-key names inside transactions, so you can't check whether the e-mail already exists).
I ended up creating a separate model with no properties, and the unique property (email address) as the key name. In the main model, I store a reference to the email model (instead of storing the email as a string). Then, I can make 'change_email' a transaction that checks for uniqueness by looking up the email by key.
This is something that I've come across as well and I settled on a variation of #Remko's solution. My main issue with checking for an existing entity with the given email is a potential race condition like op stated. I added a separate model that uses an email address as the key and has a property that holds a token. By using get_or_insert, the returned entities token can be checked against the token passed in and if they match then the model was inserted.
import os
from google.appengine.ext import ndb
class UniqueEmail(ndb.Model):
token = ndb.StringProperty()
class User(ndb.Model):
email = ndb.KeyProperty(kind=UniqueEmail, required=True)
password = ndb.StringProperty(required=True)
def create_user(email, password):
token = os.urandom(24)
unique_email = UniqueEmail.get_or_insert(email,
token=token)
if token == unique_email.token:
# If the tokens match, that means a UniqueEmail entity
# was inserted by this process.
# Code to create User goes here.
# The tokens do not match, therefore the UniqueEmail entity
# was retrieved, so the email is already in use.
raise ValueError('That user already exists.')
I implemented a generic structure to control unique properties. This solution can be used for several kinds and properties. Besides, this solution is transparent for other developers, they use NDB methods put and delete as usual.
1) Kind UniqueCategory: a list of unique properties in order to group information. Example:
‘User.nickname’
2) Kind Unique: it contains the values of each unique property. The key is the own property value which you want to control of. I save the urlsafe of the main entity instead of the key or key.id() because is more practical and it doesn’t have problem with parent and it can be used for different kinds. Example:
parent: User.nickname
key: AVILLA
reference_urlsafe: ahdkZXZ-c3RhcnQtb3BlcmF0aW9uLWRldnINCxIEVXNlciIDMTIzDA (User key)
3) Kind User: for instance, I want to control unique values for email and nickname. I created a list called ‘uniqueness’ with the unique properties. I overwritten method put in transactional mode and I wrote the hook _post_delete_hook when one entity is deleted.
4) Exception ENotUniqueException: custom exception class raised when some value is duplicated.
5) Procedure check_uniqueness: check whether a value is duplicated.
6) Procedure delete_uniqueness: delete unique values when the main entity is deleted.
Any tips or improvement are welcome.
class UniqueCategory(ndb.Model):
# Key = [kind name].[property name]
class Unique(ndb.Model):
# Parent = UniqueCategory
# Key = property value
reference_urlsafe = ndb.StringProperty(required=True)
class ENotUniqueException(Exception):
def __init__(self, property_name):
super(ENotUniqueException, self).__init__('Property value {0} is duplicated'.format(property_name))
self. property_name = property_name
class User(ndb.Model):
# Key = Firebase UUID or automatically generated
firstName = ndb.StringProperty(required=True)
surname = ndb.StringProperty(required=True)
nickname = ndb.StringProperty(required=True)
email = ndb.StringProperty(required=True)
#ndb.transactional(xg=True)
def put(self):
result = super(User, self).put()
check_uniqueness (self)
return result
#classmethod
def _post_delete_hook(cls, key, future):
delete_uniqueness(key)
uniqueness = [nickname, email]
def check_uniqueness(entity):
def get_or_insert_unique_category(qualified_name):
unique_category_key = ndb.Key(UniqueCategory, qualified_name)
unique_category = unique_category_key.get()
if not unique_category:
unique_category = UniqueCategory(id=qualified_name)
unique_category.put()
return unique_category_key
def del_old_value(key, attribute_name, unique_category_key):
old_entity = key.get()
if old_entity:
old_value = getattr(old_entity, attribute_name)
if old_value != new_value:
unique_key = ndb.Key(Unique, old_value, parent=unique_category_key)
unique_key.delete()
# Main flow
for unique_attribute in entity.uniqueness:
attribute_name = unique_attribute._name
qualified_name = type(entity).__name__ + '.' + attribute_name
new_value = getattr(entity, attribute_name)
unique_category_key = get_or_insert_unique_category(qualified_name)
del_old_value(entity.key, attribute_name, unique_category_key)
unique = ndb.Key(Unique, new_value, parent=unique_category_key).get()
if unique is not None and unique.reference_urlsafe != entity.key.urlsafe():
raise ENotUniqueException(attribute_name)
else:
unique = Unique(parent=unique_category_key,
id=new_value,
reference_urlsafe=entity.key.urlsafe())
unique.put()
def delete_uniqueness(key):
list_of_keys = Unique.query(Unique.reference_urlsafe == key.urlsafe()).fetch(keys_only=True)
if list_of_keys:
ndb.delete_multi(list_of_keys)

How to flatten a 'friendship' model within User model in GAE?

I recently came across a number of articles pointing out to flatten the data for NoSQL databases. Coming from traditional SQL databases I realized I am replicating a SQL db bahaviour in GAE. So I started to refactor code where possible.
We have e.g. a social media site where users can become friends with each other.
class Friendship(ndb.Model):
from_friend = ndb.KeyProperty(kind=User)
to_friend = ndb.KeyProperty(kind=User)
Effectively the app creates a friendship instance between both users.
friendshipA = Friendship(from_friend = UserA, to_friend = userB)
friendshipB = Friendship(from_friend = UserB, to_friend = userA)
How could I now move this to the actual user model to flatten it. I thought maybe I could use a StructuredProperty. I know it is limited to 5000 entries, but that should be enough for friends.
class User(UserMixin, ndb.Model):
name = ndb.StringProperty()
friends = ndb.StructuredProperty(User, repeated=True)
So I came up with this, however User can't point to itself, so it seems. Because I get a NameError: name 'User' is not defined
Any idea how I could flatten it so that a single User instance would contain all its friends, with all their properties?
You can't create a StructuredProperty that references itself. Also, use of StructuredProperty to store a copy of User has additional problem of needing to perform a manual cascade update if a user ever modifies a property that is stored.
However, as KeyProperty accept String as kind, you can easily store the list of Users using KeyProperty as suggested by #dragonx. You can further optimise read by using ndb.get_multi to avoid multiple round-trip RPC calls when retrieving friends.
Here is a sample code:
class User(ndb.Model):
name = ndb.StringProperty()
friends = ndb.KeyProperty(kind="User", repeated=True)
userB = User(name="User B")
userB_key = userB.put()
userC = User(name="User C")
userC_key = userC.put()
userA = User(name="User A", friends=[userB_key, userC_key])
userA_key = userA.put()
# To retrieve all friends
for user in ndb.get_multi(userA.friends):
print "user: %s" % user.name
Use a KeyProperty that stores the key for the User instance.

dynamically add fields to django models

I have a model of Pet, which looks like
class Pet(models.Model):
STATUS_CHOICES=(
(1,'Listed for sale'),
(2,'Dead'),
(3,'Sold'),
)
name = models.CharField(_("name"), max_length=50 )
species = models.ForeignKey(PetSpecies, related_name = "pets")
pet_category = models.ForeignKey(PetCategory, related_name = "pets")
pet_type = models.ForeignKey(PetType, related_name = "pets")
# want to add dynamic fields here depends on above select options(species, category, type)
color = models.CharField(_("color"), max_length=50, null=True, blank=True)
weight = models.CharField(_("weight"), max_length=50, null=True, blank=True)
i have read the Dynamic Models, would it be helpful for me? or should i do something else? if anyone know please guide me with piece of code.
thanks :)
Actually,the link you shared is not what you need...
What you need is a database table structure that can store diffrent types definitions and records related to them... In that point, you probably will need to change your database table structure...
First you may define a table that will store category labels like
class PetTyper(models.Model):
specy = models.ForeignKey(...)
category = models.ForeignKey(...)
type = models.Foreignkey(...)
...
additional_fields= models.ManyToManyField(AdditionalFields)
class AdditionalFields(Models.Model):
label = models.CharField(_("Field Label")
PetTyper is a basic record table for Pet types, So you will define each pet in this table, additional field will show which extra fields will be shown on each record. Do not forget that these tables will record basic type and additional struvcture, not records of animals added..
So a such record may contains informations like:
pettYpe: mammal, dog, Labrador Retriever , additional_info = [color, weight]
Which tells you that any dog recorded as LAbrador Retreiver will have color and weight information...
For each Labrador Retreiver recorded to the database will record data into these tables:
class Pet(models.Model):
name = models.CharField(...)
typer = models.ForeignKey(PetTyper) # this will hold records of type, so no need for specy, category and type info in this table
... # and other related fields
class petSpecifications(models.Model):
pet = Models.ForeignKey(Pet) # that data belongs to which pet record
extra_data_type = Models.ForeignKey(AdditionalFields) # get the label of the extra data field name
value = models.CharField(...) # what is that extra info value
So when yo create a new pet entry, you will define a petTyper and add names of each additional field data to AdditionalFields. In your new pet record form, you will get the pet typer first, then get each additonal info data from AdditionalFields table. User will enter a pets name after he/she chooses type, then add color and weight info (from the exapmle above). You will get those information from the form and create a record on the pet table and add each specific info about that record to the petSpecifications table...
That way is quite hard and you can not use some basic django features lke forms form models etc. Because you read data from PetTyper and AdditionalFields table and crrete your form through those information. And record the posted information to Pet and petSpecifications table...

Resources