I have the following models:
class Deal(models.Model):
date = models.DateTimeField(auto_now_add=True)
retailer = models.ForeignKey(Retailer, related_name='deals')
description = models.CharField(max_length=255)
...etc
class CustomerProfile(models.Model):
saved_deals = models.ManyToManyField(Deal, related_name='saved_by_customers', null=True, blank=True)
dismissed_deals = models.ManyToManyField(Deal, related_name='dismissed_by_customers', null=True, blank=True)
What I want to do is retrieve deals for a customer, but I don't want to include deals that they have dismissed.
I'm having trouble wrapping my head around the many-to-many relationship and am having no luck figuring out how to do this query. I'm assuming I should use an exclude on Deal.objects() but all the examples I see for exclude are excluding one item, not what amounts to multiple items.
When I naively tried just:
deals = Deal.objects.exclude(customer.saved_deals).all()
I get the error: "'ManyRelatedManager' object is not iterable"
If I say:
deals = Deal.objects.exclude(customer.saved_deals.all()).all()
I get "Too many values to unpack" (though I feel I should note there are only 5 deals and 2 customers in the database right now)
We (our client) presumes that he/she will have thousands of customers and tens of thousands of deals in the future, so I'd like to stay performance oriented as best I can. If this setup is incorrect, I'd love to know a better way.
Also, I am running django 1.5 as this is deployed on App Engine (using CloudSQL)
Where am I going wrong?
Suggest you use customer.saved_deals to get the list of deal ids to exclude (use values_list to quickly convert to a flat list).
This should save you excluding by a field in a joined table.
deals = Deals.exclude( id__in=customer.saved_deals.values_list('id', flat=True) )
You'd want to change this:
deals = Deal.objects.exclude(customer.saved_deals).all()
To something like this:
deals = Deal.objects.exclude(customer__id__in=[1,2,etc..]).all()
Basically, customer is the many-to-many foreign key, so you can't use it directly with an exclude.
Deals saved and deals dismissed are two fields describing almost same thing. There is also a risk too much columns may be used in database if these two field are allowed to store Null values. It's worth to consider remove dismissed_deals at all, and use saved_deal only with True or False statement.
Another thing to think about is move saved_deals out of CustomerProfile class to Deals class. Saved_deals are about Deals so it can prefer to live in Deals class.
class Deal(models.Model):
saved = models.BooleandField()
...
A real deal would have been made by one customer / buyer rather then few. A real customer can have milions of deals, so relating deals to customer would be good way.
class Deal(models.Model):
saved = models.BooleanField()
customer = models.ForeignKey(CustomerProfile)
....
What I want to do is retrieve deals for a customer, but I don't want to include deals that they have dismissed.
deals_for_customer = Deals.objects.all().filter(customer__name = "John")
There is double underscore between customer and name (customer__name), which let to filter model_name (customer is related to CustomerProfile which is model name) and name of field in that model (assuming CutomerProfile class has name attribute)
deals_saved = deals_for_customer.filter(saved = True)
That's it. I hope I could help. Let me know if not.
Related
Is there a by the book way of allowing a user to add columns to a sites database table. For example, if the site was about animals, one user might want to have stats like, 'walks per week' and 'type of food' about their breed of dog. but another user might want to keep track of how much milk their goat is producing.
So if i have an 'Animal' class with come basic info. like, 'breed', 'animal name', 'DOB', 'DOD'. But then, in the front end have a form that will allow the users to add all the other columns they would like.
Is this possible? hope I've explained it well enough.
#WillemVanOnsem already mentioned some good options in the comments. I'm going to chime in to say that modifying your schema's structure based on user input is an extremely bad idea and opens another avenue for abuse... for Django in particular, it means you either can't use the ORM's migration facilities for some of your models, or you probably have to do some really awful automation.
If your animal types are well-defined and consistent, you can consider (carefully) making them subclasses of the Animal model. Otherwise, this would be the simplest way to handle it (note that the following isn't valid code, it needs required arguments for the field types):
class AnimalAttribute(models.Model):
animal = models.ForeignKey(Animal)
name = models.CharField()
value = models.CharField()
This works best if attributes aren't shared, e.g. users are directly inputting their animals' names and attributes, not picking from an existing list.
If you need to provide a normalized list of attributes users can pick from (actual EAV, which is something you should avoid if possible, since it moves some of your data structure from code into the data persistence layer), doing that in your models is a little more complex. For example:
class Species(models.Model):
name = models.CharField()
class SpeciesAttribute(models.Model):
species = models.ForeignKey(Species)
name = models.CharField()
class Animal(models.Model):
name = models.CharField()
species = models.ForeignKey(Species)
class AnimalAttributeValue(models.Model):
animal = models.ForeignKey(Animal)
attribute = models.ForeignKey(SpeciesAttribute)
value = models.CharField()
Hello folks Im new to Django(I have just the finished the tutorial) but I think i understand the basic concepts of it .Im writing here because Im trying to do something "difficult" for my current experience with django and searching the internet didnt give me a solution .What im trying to do is to create a dynamic model based on the number of entries of another model .To be more exact lets say i got the following model :
class criteria(models.Model):
criteria_text = models.CharField(max_length=200)
monotonicity = models.CharField(max_length=1,choices=(('+','ASCEDING'),('-','DESCENDING')),default='+',verbose_name='Monotonicity')
worst = models.IntegerField(default=0)
best = models.IntegerField(default=0)
What i want to do is create all the criteria models instances i want through the django admin panel and then query for all the creteria_text instances in the database and make a model with an attribute for every criteria_text instance.
So lets say I add the following criteria to the database(these are criteria_text attributes of criteria objects: Color,Weight,Price .
I want to end up with a model like this :
class Alternative(models.Model):
Color = models.IntegerField(default=0)
Weight = models.IntegerField(default=0)
Price = models.IntegerField(default=0)
The thing is that in my application this one has to happen a lot of times so i cannot make model each time someone adds an Alternative based on different criteria .
After searching i found that i can define dynamic models using the following format :
attrs = {
'name': models.CharField(max_length=32),
'__module__': 'myapp.models'
}
Animal = type("Animal", (models.Model,), attrs)
So the question is how can I define "attrs" based on a query that gets all the criteria in the database ?Can i define a relationship of this dynamic model with another model ? Also the models already created should be updated if a user adds more criteria .
Is something like this possible ?
If so please show me the path .
I don't think defining dynamic models is a good solution here (or anywhere, really). Rather, you need a relationship that can have as many items as there are criteria instances.
It might be something like this:
class Alternative(models.Model):
name = models.CharField(...)
choices = models.ManyToManyField("Criteria", through="AlternativeChoice")
class AlternativeChoice(models.Model):
alternative = models.ForeignKey('Alternative')
criteria = models.ForeignKey('Criteria')
value = models.IntegerField(default=0)
The real logic will belong in the form, where you will need to create options for each criteria entry, and validate the AlternativeChoice dependent on the related criteria.
Instead of dynamically altering a models file by adding fields, very bad i've been told, i'm suppose to maintain a type of flexibility by having variable field list names(i think).
Thus, when an attribute is added to the database, this attribute can be accessed without the models file being altered.
I cant figure out how to create variable field list names in my models class though.
I'm having trouble sifting through reading materials to find a solution to my problem, and trial and era is 15hrs and counting.
Could some one point me in the right direction.
New Edit
Heres what im trying to achieve.
When an attribute is added, i add it to the table like this.
c = 'newattributename'
conn = mdb.connect('localhost', 'jamie', '########', 'website')
cursor = conn.cursor()
cursor.execute("alter table mysite_weightsprofile add column %s integer not null; SET #rank=0; UPDATE mysite_weightsprofile SET %s = #rank:=#rank+1 order by %s DESC;" % (c, c, a))
cursor.close()
conn.close()
Now, in my models class i have
class WeightsProfile(models.Model):
1attributes = models.IntegerField()
2attributes = models.IntegerField()
3attributes = models.IntegerField()
class UserProfile(WeightsProfile):
user = models.ForeignKey(User, unique=True)
aattributes = models.CharField()
battributes = models.CharField()
cattributes = models.CharField()
Now all i want to do is get access to the new attribute that was added in the table but not added to in the models file.
Does sberry2A have the right answer. I hope it is, it seems the simplest.
I might not be following what you are asking, but assuming you have some model, like Person, which will start out having some defined fields, but may have several more added in the future...
class Person(models.Model):
fname = models.CharField(max_length=255)
lname = models.CharField(max_length=255)
age = models.IntegerField()
# more fields to come
Then you could use a PersonAttribute model...
class PersonAttribute(models.Model):
name = models.CharField(max_length=32)
value = models.CharField(max_length=255)
Then you could just add a ManyToMany relationship field to your Person...
attributes = models.ManyToManyField(PersonAttribute)
Or something similar.
I don't really understand what it is you're trying to do, but South is a good system for handling changes to models. It makes migrations, so that it understands the changes you've made and knows how to change them in the database in a way that you can use for both development sites and production.
I don't understand what you're after either, JT, but I really doubt South (see #Dougal) is going to help you if what you want boils down to "Look at the relevant DB table to know what fields the model should have at read time. But not write time.". South is brilliant for evolving schemas/models, but not at runtime, and not inconsistently across rows/instances of models. And hacking models at runtime is definitely a world of hurt.
Indeed, Django's ORM isn't built for dynamic fields (at least for now) - it was built to abstract writing SQL and speed up dev against an RDBMS, not schemaless/NoSQL stuff.
Speaking of which, if someone landed me with a spec that effectively said "We don't know what fields the model will have to store" I'd suggest we try MongoDB for that data (alongside Postgres for trad relational data), probably via MongoEngine
Previously had a go at asking a more specific version of this question, but had trouble articulating what my question was. On reflection that made me doubt if my chosen solution was correct for the problem, so this time I will explain the problem and ask if a) I am on the right track and b) if there is a way around my current brick wall.
I am currently building a web interface to enable an existing database to be interrogated by (a small number of) users. Sticking with the analogy from the docs, I have models that look something like this:
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
dob = models.DateField()
class Album(models.Model):
artist = models.ForeignKey(Musician)
name = models.CharField(max_length=100)
class Instrument(models.Model):
artist = models.ForeignKey(Musician)
name = models.CharField(max_length=100)
Where I have one central table (Musician) and several tables of associated data that are related by either ForeignKey or OneToOneFields. Users interact with the database by creating filtering criteria to select a subset of Musicians based on data the data on the main or related tables. Likewise, the users can then select what piece of data is used to rank results that are presented to them. The results are then viewed initially as a 2 dimensional table with a single row per Musician with selected data fields (or aggregates) in each column.
To give you some idea of scale, the database has ~5,000 Musicians with around 20 fields of related data.
Up to here is fine and I have a working implementation. However, it is important that I have the ability for a given user to upload there own annotation data sets (more than one) and then filter and order on these in the same way they can with the existing data.
The way I had tried to do this was to add the models:
class UserDataSets(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
description = models.CharField(max_length=64)
results = models.ManyToManyField(Musician, through='UserData')
class UserData(models.Model):
artist = models.ForeignKey(Musician)
dataset = models.ForeignKey(UserDataSets)
score = models.IntegerField()
class Meta:
unique_together = (("artist", "dataset"),)
I have a simple upload mechanism enabling users to upload a data set file that consists of 1 to 1 relationship between a Musician and their "score". Within a given user dataset each artist will be unique, but different datasets are independent from each other and will often contain entries for the same musician.
This worked fine for displaying the data, starting from a given artist I can do something like this:
artist = Musician.objects.get(pk=1)
dataset = UserDataSets.objects.get(pk=5)
print artist.userdata_set.get(dataset=dataset.pk)
However, this approach fell over when I came to implement the filtering and ordering of query set of musicians based on the data contained in a single user data set. For example, I could easily order the query set based on all of the data in the UserData table like this:
artists = Musician.objects.all().order_by(userdata__score)
But that does not help me order by the results of a given single user dataset. Likewise I need to be able to filter the query set based on the "scores" from different user data sets (eg find all musicians with a score > 5 in dataset1 and < 2 in dataset2).
Is there a way of doing this, or am I going about the whole thing wrong?
edit: nevermind, it's wrong. I'll keep it so you can read, but then I'll delete afterward.
Hi,
If I understand correctly, you can try something like this:
artists = Musician.objects.select_related('UserDataSets').filter( Q(userdata__score_gt=5, userdata__id=1) | Q(userdata__sorce_lt=2, userdata__id=2 )
For more info on how to use Q, check this: Complex lookups with Q objects.
this is a model of the view table.
class QryDescChar(models.Model):
iid_id = models.IntegerField()
cid_id = models.IntegerField()
cs = models.CharField(max_length=10)
cid = models.IntegerField()
charname = models.CharField(max_length=50)
class Meta:
db_table = u'qry_desc_char'
this is the SQL i use to create the table
CREATE VIEW qry_desc_char as
SELECT
tbl_desc.iid_id,
tbl_desc.cid_id,
tbl_desc.cs,
tbl_char.cid,
tbl_char.charname
FROM tbl_desC,tbl_char
WHERE tbl_desc.cid_id = tbl_char.cid;
i dont know if i need a function in models or views or both. i want to get a list of objects from that database to display it. This might be easy but im new at Django and python so i having some problems
Django 1.1 brought in a new feature that you might find useful. You should be able to do something like:
class QryDescChar(models.Model):
iid_id = models.IntegerField()
cid_id = models.IntegerField()
cs = models.CharField(max_length=10)
cid = models.IntegerField()
charname = models.CharField(max_length=50)
class Meta:
db_table = u'qry_desc_char'
managed = False
The documentation for the managed Meta class option is here. A relevant quote:
If False, no database table creation
or deletion operations will be
performed for this model. This is
useful if the model represents an
existing table or a database view that
has been created by some other means.
This is the only difference when
managed is False. All other aspects of
model handling are exactly the same as
normal.
Once that is done, you should be able to use your model normally. To get a list of objects you'd do something like:
qry_desc_char_list = QryDescChar.objects.all()
To actually get the list into your template you might want to look at generic views, specifically the object_list view.
If your RDBMS lets you create writable views and the view you create has the exact structure than the table Django would create I guess that should work directly.
(This is an old question, but is an area that still trips people up and is still highly relevant to anyone using Django with a pre-existing, normalized schema.)
In your SELECT statement you will need to add a numeric "id" because Django expects one, even on an unmanaged model. You can use the row_number() window function to accomplish this if there isn't a guaranteed unique integer value on the row somewhere (and with views this is often the case).
In this case I'm using an ORDER BY clause with the window function, but you can do anything that's valid, and while you're at it you may as well use a clause that's useful to you in some way. Just make sure you do not try to use Django ORM dot references to relations because they look for the "id" column by default, and yours are fake.
Additionally I would consider renaming my output columns to something more meaningful if you're going to use it within an object. With those changes in place the query would look more like (of course, substitute your own terms for the "AS" clauses):
CREATE VIEW qry_desc_char as
SELECT
row_number() OVER (ORDER BY tbl_char.cid) AS id,
tbl_desc.iid_id AS iid_id,
tbl_desc.cid_id AS cid_id,
tbl_desc.cs AS a_better_name,
tbl_char.cid AS something_descriptive,
tbl_char.charname AS name
FROM tbl_desc,tbl_char
WHERE tbl_desc.cid_id = tbl_char.cid;
Once that is done, in Django your model could look like this:
class QryDescChar(models.Model):
iid_id = models.ForeignKey('WhateverIidIs', related_name='+',
db_column='iid_id', on_delete=models.DO_NOTHING)
cid_id = models.ForeignKey('WhateverCidIs', related_name='+',
db_column='cid_id', on_delete=models.DO_NOTHING)
a_better_name = models.CharField(max_length=10)
something_descriptive = models.IntegerField()
name = models.CharField(max_length=50)
class Meta:
managed = False
db_table = 'qry_desc_char'
You don't need the "_id" part on the end of the id column names, because you can declare the column name on the Django model with something more descriptive using the "db_column" argument as I did above (but here I only it to prevent Django from adding another "_id" to the end of cid_id and iid_id -- which added zero semantic value to your code). Also, note the "on_delete" argument. Django does its own thing when it comes to cascading deletes, and on an interesting data model you don't want this -- and when it comes to views you'll just get an error and an aborted transaction. Prior to Django 1.5 you have to patch it to make DO_NOTHING actually mean "do nothing" -- otherwise it will still try to (needlessly) query and collect all related objects before going through its delete cycle, and the query will fail, halting the entire operation.
Incidentally, I wrote an in-depth explanation of how to do this just the other day.
You are trying to fetch records from a view. This is not correct as a view does not map to a model, a table maps to a model.
You should use Django ORM to fetch QryDescChar objects. Please note that Django ORM will fetch them directly from the table. You can consult Django docs for extra() and select_related() methods which will allow you to fetch related data (data you want to get from the other table) in different ways.