It's pretty easy to insert data into a database by using sqlalchemy.
address.name = 'Joe'
address.age = 26
session.add(address)
But actually I have three tables - how can I specify the table I want to insert my data in?
I solved the problem by using the sql expression language to add the new row:
engine.execute(table_addresses.insert(), name='Joe', age=20)
engine.execute(table_contacts.insert(), email='joe#something.com', cellnumber='267534320')
Normaly I use the ORM but in my case it is more convinient.
You will probably want to look at the sqlalchemy ORM tutorial.
To get you started though, I suspect you are going to want to set up something like the following.
from sqlalchemy import Column, Integer, String
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
name = Column(String)
age = Column(Integer)
def __init__(self, name, age):
self.name = name
self.age = age
Now since it appears you already know how to get a session, you could just do this
address = Address('Joe', 26)
session.add(address)
session.commit() # This will do the actual database insertion
Although it is possible that you could probably just try to do it on the fly, it probably wouldn't make your life easier, as you'd still have to tell it what objects relate to integers, strings, etc, to map to the database table.
Related
class PurchaseOrder(models.Model):
purchase_order_id = models.AutoField(primary_key=True)
purchase_order_number = models.CharField(unique=True)
vendor = models.ForeignKey(Vendor)
i am creating Purchase Order(po) table. when po created i have to update purchase_order_number as "PO0"+purchase_order_id ex PO0123 (123 is Primary key). so i am using def save in models to accomplish this
def save(self):
if self.purchase_order_id is not None:
self.purchase_order_number = "PO"+str(self.purchase_order_id)
return super(PurchaseOrder, self).save()
It is working fine with single creation but when i try to create bulk of data using locust(Testing tool) its giving an error duplicate entry for PurchseOrdernumber Can we modify field value in models itself some thing like this
purchase_order_number = models.CharField(unique=True,default=("PO"+self.purchase_order_id )
To be honest, I don't think it should work when you create multiple instances. Because as I can see from the code:
if self.purchase_order_id is not None:
self.purchase_order_number = "PO"+str(self.purchase_order_id)
Here purchase_order_id will be None when you are creating new instance. Also, until you call super(PurchaseOrder, self).save(), it will not generate purchase_order_id, meaning purchase_order_number will be empty.
So, what I would recommend is to not store this information in DB. Its basically the same as purchase_order_id with PO in front of it. Instead you can use a property method to get the same value. Like this:
class PurchaseOrder(models.Model):
purchase_order_id = models.AutoField(primary_key=True)
# need to remove `purchase_order_number = models.CharField(unique=True)`
...
#property
def purchase_order_number(self):
return "PO{}".format(self.purchase_order_id)
So, you can also see the purchase_order_number like this:
p = PurchaseOrder.objects.first()
p.purchase_order_number
Downside of this solution is that, you can't make any query on the property field. But I don't think it would be necessary anyway, because you can do the same query for the purchase_order_id, ie PurchaseOrder.objects.filter(purchase_order_id=1).
Lets say i have two model
class Testmodel1():
amount = models.IntegerField(null=True)
contact = models.CharField()
entry_time = models.DateTimeField()
class Testmodel2():
name = models.CharField()
mobile_no = models.ForeignKey(Testmodel1)
and I am creating the object for this model(Testmodel2). Now I want to find out the count of object(Testmodel2) created in last 24 hours by mobile_no field.
what could be the best way of making query.
Any help would be appreciated.
It'd be better if you made the contact field into a models.DateTime field rather than a models.CharField. If it were a DateTime field, you could do lte, gte, and other operations on it easily to compare it to other datetimes.
For example, if Testmodel.contact were a DateTime field, the answer to your question would be:
Testmodel.objects.filter(contact__gte=past).count()
If the contact field contains a string representing a DateTime, I'd recommend switching it over, since there's really no reason to store it as a string.
If you're unable to change these fields, unfortunately I don't think there's a way to do this on the database level. You'll have to filter them individually on the python side:
from dateutil.parser import parse
results = []
past = arrow.utcnow().shift(hours=-24)
model_query = TestModel.objects.all()
for obj in model_query.iterator():
contact_date = parse(obj.contact) # Parse string into datetime
if contact_date > past:
results.append(obj)
print(len(results))
This will give you a list (note: NOT a queryset) containing all matching model instances. It'll be a lot slower than the other option would be, you can't edit the results afterwards with something like results.filter(amount__gte=1).count(), and it's not quite as clean.
That said, it'll get the job done.
EDIT
It occurs to me that this might be able to be done with annotation, but I'm not sure how that would be accomplished, or if it would even work. I defer to other answers if they can think of a way to use annotation to accomplish this in a better way, but stick to my original assessment that this should probably be a DateTime field.
EDIT 2
With a DateTime field now added on the other model, you can look it up across models like so:
past = arrow.utcnow().shift(hours=-24)
Testmodel2.objects.filter(mobile_no__entry_time__gte=past)
I have two tables in many to many relationship:
class Association(db.Model):
__tablename__ = 'association'
club_id = db.Column(db.Integer, db.ForeignKey('clubs.id'), primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), primary_key=True)
joined_date = db.Column(db.String)
assoc_student = db.relationship("Student")
class Club(db.Model):
__tablename__ = 'clubs'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
location = db.Column(db.String)
club_assoc = db.relationship("Association")
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
age = db.Column(db.String)
gender = db.Column(db.String)
Questions:
1) What is the difference between these two queries?
students = db.session.query(Association).filter_by(club_id='1')
students = Association.query.filter_by(club_id='1')
They seem to give the same result!
2) I'm trying to get a list of students with certain age but this following query doesn't work:
db.session.query(Association).filter_by(Association.club_id=='1', Association.assoc_student.age=='15')
But I get this error:
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Association.assoc_student has an attribute 'age'
That's why I'm using this one:
db.session.query(Student).join(Association).filter(Association.club_id=='1', Student.age=='15')
Is there a better way to do this without "join"? Maybe with using "backref"!?
1) What is the difference between these two queries?
They do almost the same thing. Former is the way to query objects provided with SQLAlchemy (library Flask uses to access database).
Latter is the convenient way to query models added by Flask-SQLAlchemy library. It makes your queries more readable + extends query with few useful methods. Take a look at source of the flask_sqlalchemy.BaseQuery class to see them: get_or_404(), first_or_404() and paginate().
Usually you want to use latter method to query objects.
2) I'm trying to get a list of students with certain age but this following query doesn't work.
There are two things here:
Be aware about the difference between filter() and filter_by() methods. In your example you try to use filter_by() with SQL expressions instead of kwargs, which is incorrect.
When you're using filter() you can't specify columns over a relationships (like Association.assoc_student.age). The only allowed format is ModelName.column_name. That's why it fails.
Is there a better way?
Your second approach is absolutely correct and fine to use. I don't think there is a better way to do it. Alternatively you can use code below to avoid importing db (if you define query in another file):
Student.query.join(Association).filter(Association.club_id == '1', Student.age == '15')
Using Django's ORM, I am trying to find instances of myModel based on two of its datetime variables; specifically, where the months of these two datetimes are not equal. I understand to filter by the value of a modelfield, you can use Django's F( ) expressions, so I thought I'd try something like this:
myModel.objects.filter(fixed_date__month=F('closed_date__month'))
I know this wouldn't find instances where they aren't equal, but I thought it'd be a good first step since I've never used the F expressions before. However, it doesn't work as I thought it should. I expected it to give me a queryset of objects where the value of the fixed_date month was equal to the value of the closed_date month, but instead I get an error:
FieldError: Join on field 'closed_date' not permitted. Did you misspell 'month' for the lookup type?
I'm not sure if what I'm trying to do isn't possible or straightforward with the ORM, or if I'm just making a simple mistake.
It doesn't look like django F objects currently support extracting the month inside a DateTimeField, the error message seems to be stating that the F object is trying to convert the '__' inside the string 'closed_date__month' as a Foreignkey between different objects, which are usually stored as joins inside an sql database.
You could carry out the same query by iterating across the objects:
result = []
for obj in myModel.objects.all():
if obj.fixed_date.month != obj.closed_date.month:
result.append(obj)
or as a list comprehension:
result = [obj for obj in myModel.objects.all() if obj.fixed_date.month != obj.closed_date.month]
Alternatively, if this is not efficient enough, the months for the two dates could be cached as IntegerFields within the model, something like:
class myModel(models.Model):
....other fields....
fixed_date = models.DateTimeField()
closed_date = models.DateTimeField()
fixed_month = models.IntegerField()
closed_month = models.IntegerField()
store the two integers when the relevant dates are updated:
myModel.fixed_month = myModel.fixed_date.month
myModel.save()
Then use an F object to compare the two integer fields:
myModel.objects.filter(fixed_month__ne=F('closed_month'))
The ne modifier will do the not equal test.
Edit - using raw sql
If you are using an sql based database, then most efficient method is to use the .raw() method to manually specify the sql:
myModel.objects.raw('SELECT * FROM stuff_mymodel WHERE MONTH(fixed_date) != MONTH(close_date)')
Where 'stuff_mymodel' is the correct name of the table in the database. This uses the SQL MONTH() function to extract the values from the month fields, and compare their values. It will return a collection of objects.
There is some nay-saying about the django query system, for example: http://charlesleifer.com/blog/shortcomings-in-the-django-orm-and-a-look-at-peewee-a-lightweight-alternative/. This example could be taken as demonstrating another inconsistency in it's query api.
My thinking is this:
class myModel(models.Model):
fixed_date = models.DateTimeField()
closed_date = models.DateTimeField()
def has_diff_months(self):
if self.fixed_date.month != self.closed_date.month:
return True
return False
Then:
[x for x in myModel.objects.all() if x.has_diff_months()]
However, for a truly efficient solution you'd have to use another column. It makes sense to me that it'd be a computed boolean field that is created when you save, like so:
class myModel(models.Model):
fixed_date = models.DateTimeField()
closed_date = models.DateTimeField()
diff_months = models.BooleanField()
#overriding save method
def save(self, *args, **kwargs):
#calculating the value for diff_months
self.diff_months = (self.fixed_date.month != self.closed_date.month)
#aaand... saving:
super(Blog, self).save(*args, **kwargs)
Then filtering would simply be:
myModel.objects.filter(diff_months=True)
Is there an easy way to fetch the ManyToMany objects from a query that returns more than one object? The way I am doing it now doesn't feel as sexy as I would like it to. Here is how I am doing it now in my view:
contacts = Contact.objects.all()
# Use Custom Manager Method to Fetch Each Contacts Phone Numbers
contacts = PhoneNumber.objects.inject(contacts)
My Models:
class PhoneNumber(models.Model):
number = models.CharField()
type = models.CharField()
# My Custom Manager
objects = PhoneNumberManager()
class Contact(models.Model):
name = models.CharField()
numbers = models.ManyToManyField(PhoneNumber, through='ContactPhoneNumbers')
class ContactPhoneNumbers(models.Model):
number = models.ForeignKey(PhoneNumber)
contact = models.ForeignKey(Contact)
ext = models.CharField()
My Custom Manager:
class PhoneNumberManager(models.Manager):
def inject(self, contacts):
contact_ids = ','.join([str(item.id) for item in contacts])
cursor = connection.cursor()
cursor.execute("""
SELECT l.contact_id, l.ext, p.number, p.type
FROM svcontact_contactphonenumbers l, svcontact_phonenumber p
WHERE p.id = l.number_id AND l.contact_id IN(%s)
""" % contact_ids)
result = {}
for row in cursor.fetchall():
id = str(row[0])
if not id in result:
result[id] = []
result[id].append({
'ext': row[1],
'number': row[2],
'type': row[3]
})
for contact in contacts:
id = str(contact.id)
if id in result:
contact.phonenumbers = result[id]
return contacts
There are a couple things you can do to find sexiness here :-)
Django does not have any OOTB way to inject the properties of the through table into your Contact instance. A M2M table with extra data is a SQL concept, so Django wouldn't try to fight the relations, nor guess what should happen in the event of namespace collision, etc... . In fact, I'd go so far as to say that you probably do not want to inject arbitrary model properties onto your Contact object... if you find yourself needing to do that, then it's probably a sign you should revise your model definition.
Instead, Django provides convenient ways to access the relation seamlessly, both in queries and for data retrieval, all the while preserving the integrity of the entities. In this case, you'll find that your Contact object offers a contactphonenumbers_set property that you can use to access the through data:
>>> c = Contact.objects.get(id=1)
>>> c.contactphonenumbers_set.all()
# Would produce a list of ContactPhoneNumbers objects for that contact
This means, in your case, to iterate of all contact phone numbers (for example) you would:
for contact in Contact.objects.all():
for phone in contact.contactphonenumbers_set.all():
print phone.number.number, phone.number.type, phone.ext
If you really, really, really want to do the injection for some reason, you'll see you can do that using the 3-line code sample immediately above: just change the print statements into assignment statements.
On a separate note, just for future reference, you could have written your inject function without SQL statements. In Django, the through table is itself a model, so you can query it directly:
def inject(self, contacts):
contact_phone_numbers = ContactPhoneNumbers.objects.\
filter(contact__in=contacts)
# And then do the result construction...
# - use contact_phone_number.number.phone to get the phone and ext
# - use contact_phone_number.contact to get the contact instance