I would like to define a foreign key with pony entities.
If I understand it right, there is no need to define a primary key as long as it is named id. so the primary key of Jobs is id, and I want to define a foreign key job_id on Recipe which is related to id of Jobs. I tried Required(Jobs.id) but this gives a type error. do you have any hint on how to do it? thank you
class Jobs(db.Entity):
path = Required(str)
date = Required(str)
class Recipe(db.Entity):
job_id = Required(int) # must be foreign
path = OptionalField(str)
date = OptionalField(str)
I found the answer. the tricky thing is that the relationship had to be defined in both classes
class Jobs(db.Entity):
path = Required(str)
date = Required(str)
jobs = Set("Recipe")
class Recipe(db.Entity):
job_id = Required(Jobs) # must be foreign
path = OptionalField(str)
date = OptionalField(str)
Related
I am failing to find a way I can create a self-referencing table using peewee. I am trying to create an entity similar to one on this article.
I have tried this solution here and it doesn't seem to give me the results that I want.
class Customer(Model):
name = TextField()
class CustomerDepartment(Model):
refid = ForeignKeyField(Customer, related_name='customer')
id = ForeignKeyField(Customer, related_name='department')
These are documented here: http://docs.peewee-orm.com/en/latest/peewee/models.html#self-referential-foreign-keys
class Department(BaseModel):
parent = ForeignKeyField('self', null=True, backref='children')
name = CharField()
Example use:
root = Department.create(name='root')
d1 = Department.create(parent=root, name='Dept 1')
# etc.
I'm new to Django and I feel sometimes it is not clear in which .py of myApp I should write solutions and examples I see.
In my models.py I have a model called Project and a model called Order. In admin section (http://127.0.0.1:8000/admin/myApp/), I would like the user to type a project number when creating a new project. Every project can have multiple Orders. I would like the Order primary key to be composed of the Project number it belongs to, plus a consecutive number. The user can only change the consecutive number part of the Oder primary key but not alter the Project number the order belongs to.
For instance for Project with project_number(primary key) = 951, Orders primary keys can be 951-1, 951-2, etc
Another project with project_number(primary key) = 1015 can also have orders 1,2, etc but they won't conflict with orders of project 951 because they will be labelled 1015-1, 1015-2, etc.
Is it possible to achieve this in models.py?
How would I have to modify order_number field below?
Notice I need the order_number field to fetch its project_number from order_project field and I won't know the order_project exact value until the user is creating the order and associating it with a project.
If what I'm asking can't be done please suggest a way to solve this and clearly explain in which .py of myApp I should write the code.
class Project(models.Model):
project_number = models.IntegerField(unique=True,primary_key=True)
project_name = models.CharField(max_length=400)
class Order(models.Model):
order_project = models.ForeignKey("Project", on_delete=models.CASCADE,verbose_name = "Project to which this order is associated",related_name= "orders_for_project")
order_number = models.CharField(unique=True,primary_key=True,max_length = 10)
UPDATE:
Following suggestions from the community my code now looks like this:
class Project(models.Model):
project_number = models.IntegerField(unique=True,primary_key=True)
project_name = models.CharField(max_length=400)
class Order(models.Model):
order_project = models.ForeignKey("Project", on_delete=models.CASCADE,verbose_name = "Project to which this order is associated",related_name= "orders_for_project")
order_number = models.IntegerField(default=1,validators=[MinValueValidator(1)])
class Meta:
#TODO capture error and present it in human readable form
constraints = [ models.UniqueConstraint(fields= ['order_project','order_number'], name = 'unique_order_id'),]
def __str__(self):
return str(self.order_project.project_number) + ("-") + str(self.order_number) + (" : ") + str(self.order_description)
I do not fully understand why my Order primary key could not be formed considering the value of the primary key of Project but this is a workaround solution
You can keep Order's primary key independent from Product's PK, just set uniqueness constraint on order_project_id + order_number combination:
class Order(models.Model):
order_project = models.ForeignKey("Project", on_delete=models.CASCADE,verbose_name = "Project to which this order is associated",related_name= "orders_for_project")
order_number = models.IntegerField()
class Meta:
unique_together = ('order_project', 'order_number')
Then if you want to display order number in {order_project_id}-{order_number} format you can just generate this value using those 2 fields in runtime.
I know I can simply update many to many relationship like this:
tags = db.Table('tags',
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True),
db.Column('page_id', db.Integer, db.ForeignKey('page.id'), primary_key=True)
)
class Page(db.Model):
id = db.Column(db.Integer, primary_key=True)
tags = db.relationship('Tag', secondary=tags, lazy='subquery',
backref=db.backref('pages', lazy=True))
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
tag1 = Tag()
tag2 = Tag()
page = Page( tags=[tag1])
and later for updating:
page.append(tag2)
but I want to update them only by the tag id, Assume I have to create a general function that only accepts person and ids for addresses and update it.
What I want is something like this:
page = Page(tags=[1,2]) # 1 and 2 are primary keys of (tag)s
or in a function
def update_with_foreignkey(page, tags=[1,2]):
# dosomething to update page without using Tag object
return updated page
It was a little tricky and by using the evil eval but finally, I find a general way to update many to many relations using foreign keys. My goal was to update my object by getting data from a PUT request and in that request, I only get foreign keys for the relationship with other data.
Step by step solution:
1- Find relationships in the object, I find them using __mapper__.relationships
2- Find the key that represents the relationship.
for rel in Object.__mapper__.relationships:
key = str(rel).rsplit('.',1)[-1]
in question case it return 'tags' as the result.
3- Find the model for another side of the relation ( in this example Tag).
3-1 Find name of the table.
3-2 Convert table name to camleCase because sqlalchemy use underscore for the table name and camelCase for the model.
3-3 Use eval to get the model.
if key in data:
table = eval(convert_to_CamelCase(rel.table.name))
temp = table.query.filter(table.id.in_(data[key])).all() # this line convert ids to sqlacemy objects
All together
def convert_to_CamelCase(word):
return ''.join(x.capitalize() or '_' for x in word.split('_'))
def update_relationship_withForeingkey(Object, data):
for rel in Object.__mapper__.relationships:
key = str(rel).rsplit('.',1)[-1]
if key in data:
table = eval(convert_to_CamelCase(rel.table.name))
temp = table.query.filter(table.id.in_(data[key])).all() # this line convert ids to sqlacemy objects
data[key] = temp
return data
data is what I get from the request, and Object is the sqlalchemy Model that I want to update.
running this few lines update give me the result:
item = Object.query.filter_by(id=data['id'])
data = update_relationship_withForeingkey(Object,data)
for i,j in data.items():
setattr(item,i,j)
db.session.commit()
I'm not sure about caveats of this approach but it works for me. Any improvement and sugesstion are welcome.
I would like to have an entity as follows:
class EntitySharedLinkPermission(models.Model):
OFF = None
COMPANY_VIEW = "Company View"
COMPANY_EDIT = "Company Edit"
PUBLIC_VIEW = "Public View"
PUBLIC_EDIT = "Public Edit"
name = models.CharField(max_length=12, primary_key=True)
class Meta: db_table = 'entity_shared_link_permission'
However, I cannot have NULL as a primary key value here. What should I do here instead? One idea was to just remove the primary key on this table and have a unique key instead (no PK in the table) to get around this, but surely there must be a better solution.
Simply put, you can't have null as primary key column value. You should always supply non null value to the primary key. Also, don't go for unique, it just isn't the solution though it masquerades as being one. If you can't always supply non null value, introduce a new identity column to your table instead.
If you don't expect the list of items to change often, and the set is small, then it looks to me like you're trying to set up a "choices" field, for which Django already has nice support. Here's the example that the Django docs use, which you could easily adapt to your situation:
from django.db import models
class Student(models.Model):
FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
YEAR_IN_SCHOOL_CHOICES = (
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
)
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)
def is_upperclass(self):
return self.year_in_school in (self.JUNIOR, self.SENIOR)
However, if you expect the list of permissions to be fluid and change often, you should consider making the permissions a Model of their own, and simply use a ForeignKey (or ManyToMany) relationship,
I have two tables, Name and Person.
Name:
id (int, primary key)
name (varchar)
Person:
id (int, primary key)
name_id (int, foreign key->Name.id)
Assuming my models are set up with the foreign keys, if I run Person.query.first().name_id, this will return an integer. I want it to return the name varchar. Is this possible? Or is there something I can do to get the same result?
class Name(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text) # or varchar
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name_id = db.Column(db.Integer, db.ForeignKey('name.id'))
_name = db.relationship('Name')
#property
def name(self):
return self._name.name
Getting the actual name back could be done like this or with a select in the name function. I prefer this way with a property. You'll need to fill in the details with how you are using joins in the relationship and other details.
You could #property to#hybrid_property to get some neat functionality from SQLAlchemy. Of course, you need to use it effectively.