I want to create an order form to buy multiple things in this sort of structure:
Business Data 1
---Product A
---Product B
Business Data 2
---Product A
That is, I want to sell products A,B, but before this is done I need additional aggregate data to be in the 'Business Data' object. That is:
Business 1: Joe's Plumbing, located at ... ... ...
---Product A, standard ad appearing in category 3, with text "awesome plumbing"
---Product B, cooler ad appearing in category 9, with text "cheap plumbing"
Business 2: Joe's Burgers, located at ... ... ...
---Product A, standard ad appearing in category 4, with text "zzz"
or, from a model level, more like:
class Business(models.Model):
name = models.CharField(max_length=255)
address = models.MagicAddressField()
class Ad(models.Model):
category = models.ForeignKey(Category)
text = models.CharField(max_length=255)
business = models.ForeignKey(Business)
Now, instead of reimplementing an entire shopping cart backend, I'm pretty sure I want to use Satchmo. However, i'm having trouble wrapping my head around this sort of thing.
Satchmo appears to have multiple options for Products, but they're all "flat". That is, while I could easily get Satchmo to allow the end user to buy Product A and Product B, the db shows no connection to Business 1, and things like the business name would have to be repeated in both product A and B.
I think I can probably get around this with my own views / templates, if only I can get the final "product instance" that satchmo is selling during an order to have a foreign key to the Business table i'll create myself. In other words, I want to make the Ad model a satchmo custom product model - I think...
But If I just change Ad to:
class Ad(Product):
objects = ProductManager()
category = models.ForeignKey(Category)
text = models.CharField(max_length=255)
business = models.ForeignKey(Business)
Isn't this the wrong semantics? Doesn't that mean that "this product type is associated with business x", not "when you buy this, the product's instance will be pointing to business x"?
I'm pretty confused here :-/
If I understood this right, I would make something like this.
Make a ManyToMany relationship between business and the products:
class Business(models.Model):
name = models.CharField(max_length=255)
address = models.MagicAddressField()
products = models.ManyToManyField(Product)
Then, on your custom-form and template/view, you can add for each business a fieldset with the Products/Ads for each business. The only complicatation I see by now, would be, that after filling up the form and sending it, you would also need customized "checkout-process" to save the information of which product got bought through which business... maybe this could be done through product-options(?) but any way it'll probably mean a lot of customization/working with signals ;-) But i think this is the right direction.
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()
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.
I have a probably quite basic question: I am currently setting up a database for students and their marks in my courses. I currently have two main classes in my models.py: Student (containing their name, id, email address etc) and Course (containing an id, the year it is running in and the assessment information - for example "Essay" "40%" "Presentation" "10%" "Exam" "50%"). And, of course, Student has a ManyToMany field so that I can assign students to courses and vice versa. I have to be able to add and modify these things.
Now, obviously, I would like to be able to add the marks for the students in the different assignments (which are different from course to course). As I am very unexperienced in database programming, I was hoping one of you could give me a tip how to set this up within my models.
Thanks,
Tobi
Perhaps the way to go about it is to have a separate class for assignment, something like this.
class Assignment(models.Model):
ASSIGNMENT_TYPES = (
('essay', "Essay"),
...
)
ASSIGNMENT_GRADES = (
('a+', "A+"),
('a', "A"),
...
)
student = models.ForeignKey("Student")
course = models.ForeignKey("Course")
assignment_type = models.CharField(choices=ASSIGNMENT_TYPES, max_length=15, default='essay')
progress = models.IntegerField()
grade = models.CharField(choices=ASSIGNMENT_GRADES, max_length=3, default="a+")
This way you have one assignment connected to one student and one course. It can be modified relatively easy if you have multiple students per one assignment, by adding another class (for example StudentGroup) and including it in the model.
Hope that this helps :)
Create a model called "Assessments", which has a foreign key to Course. In addition ,create a field called "Assessment Type", another called "Assessment Result" and a final one called "Assesment Date". Should look like this:
ASSESSMENTS = (('E','Essay'),('P','Presentation'))
class Assessment(models.MOdel):
course = models.ForeignKey('Course')
assessment = models.CharField(choices=ASESSMENTS)
result = models.CharField(max_length=250)
taken_on = models.DateField()
overall_result = models.BooleanField()
is_complete = models.BooleanField()
Each time there is an exam, you fill in a record in this table for each assessment taken. You can use the overall result as a flag to see if the student has passed or failed, and the is_complete to see if there are any exams pending for a course.
You should look at models.py file of classcomm,
a content management system written in Django for delivering and managing Courses on the Web.
It has following Models
Department
Course
Instructor
Mentor
Enrollment
Assignment
DueDateOverride
Submission
Grade
ExtraCredit
Information
Resource
Announcement
You may not need such a complex relationship for you case, but it's wort looking into it's models design.
You can find more details on homepage of this project.
I have a few models that look like this:
class System(models.Model):
'''Defines a system'''
system_desc = models.CharField('System Name', max_length=50)
class SystemGroup(models.Model):
'''Groups within a 'System' (ie. Customers, regions, divisions, etc. '''
group_desc = models.CharField('Group Name',max_length=25)
system = models.ForeignKey(System)
class QueryData(models.Model):
'''Queries related to a 'System' (ie. Active users against System1, Orders today in system2, etc. '''
qry_desc = models.CharField('Query Description', max_length=50)
system = models.ForeignKey(System)
class UserDashboard(models.Model):
'''User specific Dashboard '''
user = models.ForeignKey(User)
board_name = models.CharField('Dashboard Name', max_length=50)
class UserDashboardGroup(models.Model):
'''Groups on the dashboard (ie. 'System 1's Key Indicators', 'System 2's Hot Sheet', etc. '''
group_desc = models.CharField('Group Display Title', max_length=50)
user = models.ForeignKey(User)
dashboard = models.ForeignKey(UserDashboard)
system = models.ForeignKey(System)
system_group = models.ForeignKey(SystemGroup)
class UserGroupQuery(models.Model):
'''Queries that run in a specific group on the dashboard (ie. Within 'System 1's Key Indicators, we run queries for active users today, orders in the last hour, etc. '''
user = models.ForeignKey(User)
dashboard = ChainedForeignKey(UserDashboard, chained_field='user', chained_model_field='user', show_all=False, auto_choose=True)
user_dashboard_group = ChainedForeignKey(UserDashboardGroup, chained_field='dashboard', chained_model_field='dashboard')
query = models.ForeignKey(QueryData)
I am having problems with the very last part of this. I want to restrict the 'query' that appears in a admin page based on the selected user_dashboard_group. I'm not sure how I can do this, based on my current models though. query and user_dashboard_group both have a foreign key to System, but not to each other. To get this though, I'd have to get the user_dashboard_group's system and then use that to filter the QueryData. How can I do this?
Edit
I'm adding in a picture to (hopefully) describe a little better what I want to do.
In step 1, the user inputs a name for this group of queries. This group is associated with a system (#2) and a predefined group within the system (#3) (think of #3 as a 'customer' or a 'region', etc and #1 and #3 are NOT the same thing, despite the similar naming). They then select 'Save and Continue editing' on this inline form and the drop down at step 4 becomes populated with information from the above form. Once step #4 has a selection made, I want #5 to populate with data only from the associated system. Since #2 contains this information, I am hoping it is fairly easy to do this, but I can't figure out the chaining.
I also realized that I didn't mention I was using django-smart-selects
I have never worked with django-smart-selects, but following the docs, I would expect
query = models.ChainedForeignKey(QueryData,**kwargs)
instead of
query = models.ForeignKey(QueryData)
since you want the options in query depend on the other selections. Is that understanding correct? If so, it was only about defining the right **kwargs.
For **kwargs, I would propose something like this
Update
chained_field='query', chained_model_field='user_dashboard_group__system__query_set__query'
assuming that the fields name is 'system in both cases.
I am pretty sure that this describes the relationship correctly. I am just not sure, if django-smart-selects supports that syntax.
Same way you would do it across one relation, except across two.
field__relation1__relation2
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.