I have a Django view that uses one form multiple times. The form is saving relationship Subgroup id as a foreign key and Student id as a foreign key .
The problem I'm having is when I try to save information to database it only saves the last record.
For example (database model):
1 858 | Pump | Iron
2 78 | Madagaskar| Thomas
And if Im trying to split them into seperate groups, only Madagaskar his data is saved:
id | timestamp | student_Id_id | subgroup_Id_id |
+----+----------------------------+---------------+----------------+
| 62 | 2016-05-06 10:54:49.022000 | 2 | 91 |
The form looks like this:
class ApplicationFormaFull1(MultiModelForm):
form_classes = {
'sub1': FormSubgroup,
'sub2': FormSubgroup,
'stud_sub': FormStudent_in_Subgroup
}
and my view :
sub = form['sub1'].save(commit=False)
sub.student_group = StudentGroup.objects.get(id=element)
sub.number = 1
sub.type = 'Other'
sub.student_count = firstSubgroup
sub.save()
sub1 = form['sub2'].save(commit=False)
sub1.student_group = StudentGroup.objects.get(id=element)
sub1.number = 2
sub1.type = 'Others'
sub1.student_count = secondSubgroup
sub1.save()
if (counter%2==1):
stud_sub = form['stud_sub'].save(commit=True)
stud_sub.subgroup_Id = sub
stud_sub.student_Id = Student.objects.get(id=student)
stud_sub.save()
else:
stud_sub = form['stud_sub'].save(commit=True)
stud_sub.subgroup_Id = sub1
stud_sub.student_Id = Student.objects.get(id=student)
stud_sub.save()
So to sum up, I want that every form would save its information multiple times (dynamically)
Maybe the solution is that I should store information in the list and after all forms are added, save them one by one ?
stud_sub = form['stud_sub'].save(commit=False)
stud_sub.subgroup_Id = sub
stud_sub.student_Id = Student.objects.get(id=student)
list.add(stud_sub)
...
for i in list:
i.save()
Other solution use formset:
ArticleFormSet = formset_factory(ArticleForm, extra=2)
formset = ArticleFormSet(initial=[
{'title': 'Django is now open source',
'pub_date': datetime.date.today(),}
])
However i dont know how to change title, pub_date and to add everyting to formset dynimically.
Related
I saw that it is possible to access data from context.table from Behave when the table described in the BDD has a header. for example:
Scenario: Add new Expense
Given the user fill out the required fields
| item | name | amount |
| Wine | Julie | 30.00 |
To access this code it's simply:
for row in context.table:
context.page.fill_item(row['item'])
context.page.fill_name(row['name'])
context.page.fill_amount(row['amount'])
That works well and it's very clean, however, I have to refactor code when I have a huge amount of lines of input data. for example:
Given I am on registration page
When I fill "test#test.com" for email address
And I fill "test" for password
And I fill "Didier" for first name
And I fill "Dubois" for last name
And I fill "946132795" for phone number
And I fill "456456456" for mobile phon
And I fill "Company name" for company name
And I fill "Avenue Victor Hugo 1" for address
And I fill "97123" for postal code
And I fill "Lyon" for city
And I select "France" country
...
15 more lines for filling the form
How could I use the following table in behave:
|first name | didier |
|last name | Dubois |
|phone| 4564564564 |
So on ...
How would my step definition look like?
To use a vertical table rather than a horizontal table, you need to process each row as its own field. The table still needs a header row:
When I fill in the registration form with:
| Field | Value |
| first name | Didier |
| last name | Dubois |
| country | France |
| ... | ... |
In your step definition, loop over the table rows and call a method on your selenium page model:
for row in context.table
context.page.fill_field(row['Field'], row['Value'])
The Selenium page model method needs to do something based on the field name:
def fill_field(self, field, value)
if field == 'first name':
self.first_name.send_keys(value)
elif field == 'last name':
self.last_name.send_keys(value)
elif field == 'country':
# should be instance of SelectElement
self.country.select_by_text(value)
elif
...
else:
raise NameError(f'Field {field} is not valid')
after an python3 manager.py inspectdb (mysql) and a look on few helps and tuto, i still have somes mistakes and incorrect results.
models.py
class A(models.Model):
ida = models.AutoField(db_column='idA', primary_key=True)
col1 = #an another column
has_B = models.ManyToManyField(B, related_name='a', through="AHasB", through_fields=('a_ida', 'b_idb'))
#I had add this line after a tuto in django book for the manytomayfield
class B(models.Model):
idb = models.AutoField(db_column='idB', primary_key=True)
col1 = #an another column
class AHasB(models.Model):
a_ida = models.ForeignKey(A)
b_idb = models.ForeignKey(B)
col1 = #an another column
view.py
def myview(request):
for element in b.filter(idb__in=a.values('has_B').distinct()):
print(element)
In my database i have,
A :
ida | col1
1 | ...
2 | ...
3 | ...
B :
idb | col1
1 | ...
AHasB :
a_ida | b_idb
1 | 1
But when i will display result (ida -> idb) like a classic (SELECT idb,ida FROM A, B, AHasB WHERE AHasB.a_ida=A.ida AND AHasB.b_idb=B.idb), i have this ...
1 -> 1
2 -> 1
3 -> 1
And in normal case, i will just have 1 -> 1.
Maybe the model dont fit with my real database in back.
Edit
view.py
def myview(request):
a = A.objects.All()
b = B.objects.All()
for element_a in a.filter("somefilters"):
in_has_b = set(AHasB.objects.values_list('b_idb', flat=True));
print(b.filter(idb__in=in_has_b))
If you want to select all B instances that are referenced / owned by each A instance:
bs_for_each_a = {}
for a in A.objects.all():
bs = AHasB.objects.filter(a_ida=a).values('b_idb')
bs_for_each_a[a] = bs
If you need distinct Bs, you can try to add .distinct() after values('b_idb') but I did not test it. Another way to remove duplicates would be to use values_list('b_idb', flat=True) and pass the result of the query in set():
for a in A.objects.all():
bs = AHasB.objects.filter(a_ida=a).values_list('b_idb', flat=True)
bs_for_each_a[a] = set(bs)
I've got a question which I'll ask two ways: short & generic, so future generations of StackOverflow readers will benefit, and Long & Detailed, so I can get my work done without screwing anything up.
Short & Generic Version:
How do I make Django generate a table-like form where some info in the table is from the database, and the user fills in the rest? On form submission, each row in the table should become a record in the database (after it's validated, of course).
What's the cleanest way to do this? What's this mode of interaction cannonically called?
Example Form
|=============================================================|
| QueriedValue | CalculatedValue | User_data | More_User_data |
|_____________________________________________________________|
| Foo 1 | Bar 1 | | |
| Foo 2 | Bar 2 | | |
| Foo 3 | Bar 3 | | |
... ... ... ... |
| Foo n | Bar n | | |
|=============================================================|
++++++++++
| Submit |
++++++++++
Resulting Database Records
TimeStamp + fk_Foo = natural primary key for this table
________________
/ \
|===========================================================|
| TimeStamp | fk_Foo | User_data | More_User_data |
|___________________________________________________________|
| submit_time | Foo 1 | Datum 1 | AnotherDatum 1 |
| submit_time | Foo 2 | Datum 2 | AnotherDatum 2 |
| submit_time | Foo 3 | Datum 3 | AnotherDatum 3 |
|... ... ... ... |
| submit_time | Foo n | Datum n | AnotherDatum n |
|===========================================================|
Long Version
I'm writing a web app to track gas cylinder usage at my company. We have a bunch of gas plumbing in our building, and we need to know which gas cylinder was hooked up to which gas line at what time.
I'd like two forms for the technicians to fill out:
Daily Inventory: Every morning, the stockroom guy needs to look at each gas line and record the line's pressure, and the reference number of the bottle. This generates bunch of 4-tuple records (time, line, bottle, psi); one for each line, every morning.
As-Needed Bottle Change: After doing the daily inventory, if a bottle is almost out it needs to be changed, and that change needs to be logged. This should add another entry to the table of bottles for the new bottle, and another 4-tuple with the new (time, line, bottle, psi) info for the new connection. This happens to a random line a few times a week, but not every day.
So to keep track of this I'm writing a Django application. I've got the following models:
# models.py
class GasFarm(models.Model):
"""
Represents a gas farm -- a collection of lines that are grouped together and managed as a unit.
"""
name = models.CharField(max_length=30, unique=True)
def __unicode__(self):
return self.name
class Bottle(models.Model):
"""
Represents a gas bottle -- the physical cylinder -- that contains a mixture of gases.
"""
# Options
get_latest_by = 'date_added'
# Fields
BACKGROUND_TYPES = (
('h2/n2', "H2/N2"),
('h2/air', "H2/Air"),
('h2', "H2"),
('n2', "N2"),
('other', "Other"),
)
ppm = models.FloatField()
mix = models.CharField(max_length=50, choices=BACKGROUND_TYPES, default='n2')
ref = models.CharField(max_length=50, unique=True) # Every bottle has a unique ref or somebody fucked up.
cert_date = models.DateTimeField()
date_added = models.DateTimeField(default=timezone.now())
def pct(self):
return float(self.ppm)/10**4
def __unicode__(self):
return "{} ({}% {})".format(self.ref, self.pct(), self.mix,)
class Line(models.Model):
"""
Represents a gas line -- the physical plumbing -- that delivers gas from the bottles to the test stations.
It is assumed that a gas line can have zero or one gas bottles attached to it at any given time. The Line model
maps bottle objects and time-sensitive Reading objects to test stations.
"""
# Fields
gasfarm = models.ForeignKey(GasFarm)
number = models.CharField(max_length=10, unique=True)
bottles = models.ManyToManyField(Bottle, through='Reading')
# Calculated fields. "current" is definitely not optional -- that's a super common query. The others? I'm not so
# sure...
def current(self):
"""
Returns the most recently recorded Reading object associated with the line
"""
return self.reading_set.latest(field_name='time')
current.short_description = "latest reading"
def last_checked(self):
"""
Returns the date & time at which the most recent Reading object associated with this line was logged
"""
return self.current().time
last_checked.short_description = "last updated"
def has_recent_reading(self):
"""
Boolean flag for whether the reading is probably valid, or if someone needs to go out and take a new one.
"""
latest_reading = self.current().time
return timezone.now() - latest_reading < datetime.timedelta(days=3)
has_recent_reading.boolean = True
has_recent_reading.short_description = "Is data current?"
def __unicode__(self):
return self.number
class Reading(models.Model):
"""
A Reading links a Bottle to a Line at a given time, and provides a snapshot of the pressure at that time.
"""
# Options
get_latest_by = 'time'
# Fields
line = models.ForeignKey(Line)
bottle = models.ForeignKey(Bottle)
time = models.DateTimeField()
psi = models.IntegerField(validators=[MaxValueValidator(2500)])
def ref(self):
"""
The reference number of the bottle listed in the reading
"""
return self.bottle.ref
def ppm(self):
"""
The PPM concentration of the bottle listed in the reading
"""
return self.bottle.ppm
def pct(self):
"""
The % concentration of the bottle listed in the reading
"""
return self.bottle.pct()
def mix(self):
"""
The gas mix (e.g. H2/N2) of the associated bottle
"""
return self.bottle.mix
def __unicode__(self):
# Example:
# A0: 327.3 PPM H2/N2 2300 psi
return "{}, {}: {} PPM {} {} psi".format(self.line, self.time, self.ppm(), self.mix(), self.psi)
I've populated the database with our back-log of data using some scripts, and I've written a few views to pull data out of the databse; I'm happy with them so far, and the results look very promising -- at least for displaying stored data.
But I'm not sure how to cleanly populate the database using HTML forms. I'd like the forms to be basically two separate "worksheets" -- like the kind the DMV gives you, with nice clear instructions #justkidding.
Form 1: Daily Inventory
The form would list all the lines in a given farm, display what bottle should be on each line (based on previous readings/updates), and then prompt the user to enter a value. This would require that the technician update the pressure of every bottle on every line each time they submit the form -- we want a global snapshot of the whole gas system. In a perfect world, the form would pre-fill the current time and each line's most recent pressure reading into the Reading Time and Pressure fields to ease data entry.
# Cells with brackets [] are system-supplied, non-editable data displayed in the table.
# Cells without brackets are pre-filled with sensible defaults, but are user editable.
| [Line] | [Current Bottle] | Reading Time | Pressure (psi) |
===============================================================
| [A0] | [15-1478334] | 2014-7-14 9:34 | 2400 |
| [A1] | [15-1458661] | 2014-7-14 9:34 | 500 |
| [A2] | [15-4851148] | 2014-7-14 9:34 | 1850 |
| [A3] | [15-1365195] | 2014-7-14 9:34 | 700 |
...
...
| [C18] | [15-9555813] | 2014-7-14 9:34 | 2350 |
|=====================================================================|
After reading through the Django docs on Forms, ModelForms, and Formsets, I've written some code that does almost everything I want -- but the Line and Bottle information are editable form fields, and I need them to be static guideposts for filling in the rest of the form. They do need to be present in the generated database records, though.
I am dimly aware of the readonly and disabled attributes, and of what appear to be kludgy solutions to clean data from the POST variable in the response when you want to have read-only stuff in forms, but I'm still not clear on how those work or why they're necessary. I'm wondering if there's a cleaner way to get what I"m after? Perhaps forms with programmatically generated headings, or annotations? That's all I really want: an auto-generated guide to filling out the form.
# Forms.py
class PressureReadingUpdate(forms.ModelForm):
class Meta:
model = models.Reading
PsiReadingFormset = formset_factory(PressureReadingUpdate, extra=0)
# views.py
def update_pressure(request):
if request.method == 'POST':
formset = forms.PsiReadingFormset(request.POST)
if formset.is_valid():
cd = formset.cleaned_data
# do something? I'm not here yet...
else:
lines = models.Line.objects.all()
now = datetime.datetime.now()
initial = [{'line': l,
'psi': l.current().psi,
"bottle": l.current().bottle,
'time': now} for l in lines]
formset = forms.PsiReadingFormset(initial=initial,)
return render(request, 'gas_tracking/gasfarm_form_psi_reading.html', {'formset': formset})
Form 2: Change a Gas Bottle
I'd like a list of all the gas lines, with the current bottle & pressure (easy-- this is done elsewhere), and then a button that makes a pop-up window where you can submit a new bottle, much like you find in the admin interface. How do I make pop-up windows? How do I make buttons? I'm not even sure where to start with this one yet
I'm still very new to Django, and I've searched high and low, but haven't found anything that answers my question -- maybe I'm just not using the right keywords?
Thanks for your help.
-Matt
Dynamically generating Forms using FormSets
So I figured this out (after much googling and swearing and gnashing of teeth). Malcolm Tredinnick made a blog post about exactly what I wanted to do, which a kind soul preserved on Django Snippets
Using Malcom's code as a model, I solved my problem and it works like a charm
class PressureReadingUpdate(forms.Form):
"""
Form that asks for the pressure of a line given some attributes of that line.
"""
psi = forms.IntegerField(widget=forms.NumberInput)
def __init__(self, *args, **kwargs):
self.line = kwargs.pop('line')
kwargs['auto_id'] = "%s".format(self.line.number)
super(PressureReadingUpdate, self).__init__(*args, **kwargs)
class BasePsiReadingFormset(BaseFormSet):
"""
Formset that constructs a group of PressureReadingUpdate forms by taking a queryset
of Line objects and passing each one in turn to a PressureReadingUpdate form as it
gets constructed
"""
def __init__(self, *args, **kwargs):
self.lines = kwargs.pop('lines')
super(BasePsiReadingFormset, self).__init__(*args, **kwargs)
self.extra = len(self.lines)
self.max_num = len(self.lines)
def _construct_form(self, i, **kwargs):
kwargs['line'] = self.lines[i]
form = super(BasePsiReadingFormset, self)._construct_form(i, **kwargs)
return form
PsiReadingFormset = formset_factory(form=PressureReadingUpdate, formset=BasePsiReadingFormset)
This gives you a Formset with an extra kwarg you can pass down the chain of constructors. You can use it in a view (along with a more typical initial= kwarg) with:
formset = PsiReadingFormset(lines=lines,
initial=[{'psi': l.current().psi} for l in lines],
So here's the best explanation I can think of:
Any kwargs passed to a FormSet like (which gets made by the formset_factory function using a non-default 'BaseFormSet' as a blueprint) get passed along -- mostly unaltered -- to the __init__ method of whatever BaseFormSet the FormSet is based on.
This means you can define custom behavior in BaseFormSet.__init__, and you can relay runtime data to the BaseFormSet.__init__ method by passing it as a keyword argument to FormSet (that's the lines= kwarg in my example above). I use it to set an attribute on on formset (an instance of a FormSet based on 'BasePsiReadingFormset').
Confused yet? I was too at first.
But the real magic is understanding how _construct_forms works: A FormSet calls this function each time it wants to make a new Form in the set. It will relay any unrecognized kwargs to to the constructor of the Form it is supposed to manage a set of.
So you just need to overload your custom BaseFormSet's ._construct_forms to wrap the original _construct_forms of the superclass and inject a new kwarg. This kwarg will then be passed through to the constructor of your custom Form class, and shape the new Form instance according to the Form's initialization function.
And that, ladies and gentlemen, is how you can have a FormSet that has a bunch of similarly-defined but dynamically-generated-and-slightly different forms in it.
After understanding this, I can see the elegance of it. However, it uses some intermediate-to-advanced python, and is neither immediately obvious nor well documented. If you are struggling with this like I did, feel free to message me.
I'm trying to learn Peewee and Bottle by making a book note-taking application.
Say I have the following entities:
Subject
Book
Chapter
Note
Tag
I would like to be able to make Notes for Chapters, Books, and Subjects.
In a DB, you would do:
create table noteable (
noteable_id INT AUTO_INCREMENT PRIMARY KEY
,type VARCHAR(10) NOT NULL CHECK (type in ('SUBJECT','BOOK','CHAPTER','NOTE'))
);
create table subject (
subject_id INT AUTO_INCREMENT PRIMARY KEY
,noteable_id INT UNIQUE REFERENCES noteable (noteable_id)
,...
);
create table book (
book_id INT AUTO_INCREMENT PRIMARY KEY
,subject_id INT NOT NULL REFERENCES subject (subject_id)
,noteable_id INT UNIQUE REFERENCES noteable (noteable_id)
,...
);
create table chapter(
chapter_id INT AUTO_INCREMENT PRIMARY KEY
,book_id INT NOT NULL REFERENCES book (book_id)
,noteable_id INT UNIQUE REFERENCES noteable(noteable_id)
,...
);
create table note(
note_id INT AUTO_INCREMENT PRIMARY KEY
,noteable_id INT UNIQUE REFERENCES noteable(noteable_id)
,...
);
(If you wanted a M:N relationship between note and notable, you would do a note_notable bridge table as well).
You would have before insert triggers on subject, book, and chapter that would insert a row into noteable, retrieve the new row's noteable_id, and use that on the incoming row.
I'm assuming that if you are using an ORM like Peewee you would want to do that in application logic rather than triggers.
How can I implement this model in Peewee?
Here is how I did it. I couldn't find a native way in Peewee to implement inheritance so I just rolled it myslef. If there is a better way, please provide your answer and I'll award it.
import MySQLdb
import peewee
from peewee import *
from datetime import datetime
db = MySQLDatabase('test', user='root',passwd='psswd')
class BaseModel(Model):
class Meta:
database = db
class Noteable(BaseModel):
type = CharField(null = False)
# This will act as the trigger that inserts a row into noteable,
# and retrieves the notable.id to use
class N(BaseModel):
def save(self, *args, **kwargs):
if not self.id:
noteable = Noteable(type=self.__class__.__name__.upper())
noteable.save()
self.noteable = noteable.id
return super(N, self).save(*args, **kwargs)
class Subject(N):
name = CharField(null = False, unique = True)
noteable = ForeignKeyField(Noteable, related_name="noteablesubject", null= False, unique = True)
class Book(N):
name = CharField(null = False, unique = True)
subject = ForeignKeyField(Subject, related_name="books", null = False)
noteable = ForeignKeyField(Noteable, related_name="noteablebook", null= False, unique = True)
class Chapter(N):
name = CharField(null = False)
chapter_number = IntegerField(null = False)
book = ForeignKeyField(Book, related_name="chapters")
noteable = ForeignKeyField(Noteable, related_name="noteablechapter", null= False, unique = True)
class Note(BaseModel):
note = TextField(null = False)
# N.B. unique is not true, as multiple notes can go to the same subject/book/chapter
noteable = ForeignKeyField(Noteable, related_name="notes", null= False)
Note.drop_table(True)
Chapter.drop_table(True)
Book.drop_table(True)
Subject.drop_table(True)
Noteable.drop_table(True)
Noteable.create_table(True)
Subject.create_table(True)
Book.create_table(True)
Chapter.create_table(True)
Note.create_table(True)
s = Subject(name="subject")
s.save()
n = Note(note="subject notes", noteable = s.noteable)
n.save()
n = Note(note="subject notes 2", noteable = s.noteable)
n.save()
b = Book(name="book", subject=s)
b.save()
n = Note(note="book notes", noteable = b.noteable)
n.save()
n = Note(note="book notes 2", noteable = b.noteable)
n.save()
c = Chapter(chapter_number=1, name="chapter", book=b)
c.save()
n = Note(note="chapter notes", noteable=c.noteable)
n.save()
n = Note(note="chapter notes 2", noteable=c.noteable)
n.save()
(if you wished to have a many to many relationship between notes and notable, you would have to define a NoteNotable class with foreign keys and remove the FK from Note)
You can define a helper method to left join whichever class with notes:
def get_notes(clazz, id):
return clazz.select().join(Noteable).join(Note, JOIN_LEFT_OUTER).where(clazz.id = id)
You can iterate over it like:
% for note in chapter.noteable.notes:
% end
Here are the results from a SELECT * FROM NOTABLE;
+----+---------+
| id | type |
+----+---------+
| 1 | SUBJECT |
| 2 | BOOK |
| 3 | CHAPTER |
+----+---------+
Here are the results from a SELECT * FROM NOTE;
+----+-----------------+-------------+
| id | note | noteable_id |
+----+-----------------+-------------+
| 1 | subject notes | 1 |
| 2 | subject notes 2 | 1 |
| 3 | book notes | 2 |
| 4 | book notes 2 | 2 |
| 5 | chapter notes | 3 |
| 6 | chapter notes 2 | 3 |
+----+-----------------+-------------+
Django Code & Reference to Django Bug Report
Given three models as follows (simplified excessively for demonstration...not actually identical related models)
class derp(models.Model):
...
class derp_related_1(models.Model):
fk = models.ForeignKey(derp)
amount = models.DecimalField(max_digits=15, decimal_places=2)
class derp_related_2(models.Model):
fk = models.ForeignKey(derp)
amount = models.DecimalField(max_digits=15, decimal_places=2)
And overriding a queryset in the model admin as follows. (It isn't working because of this django bug.)
class DerpAdmin(admin.ModelAdmin):
...
list_display = ['derp_r1_sum', 'derp_r2_sum']
...
def queryset(self, request):
qs = super(DerpAdmin, self).queryset(request)
qs = qs.annotate(derp_r1_sum=models.Sum('derp_r1__amount', distinct=True))
qs = qs.annotate(derp_r2_sum=models.Sum('derp_r2__amount', distinct=True))
def derp_r1_sum(self, obj):
return u'%s' % obj.derp_r1_sum
def derp_r2_sum(self, obj):
return u'%s' % obj.derp_r2_sum
Example of Unexpected Database Result
Running annotations individually would render something like (with grouping & sums removed)
+---------+--------+
| derp.id | r1_sum |
+---------+--------+
| 2 | 500.00 |
| 2 | 100.00 |
+---------+--------+
r1_sum would be 600.00
and
+---------+--------+
| derp.id | r1_sum |
+---------+--------+
| 2 | 100.00 |
| 2 | 250.00 |
+---------+--------+
r2_sum would be 350.00
If you take qs.query with both annotations included and remove the sums and the grouping it is obvious what the problem is. In this case we're counting everything twice. Get more relations and we have an increasingly ugly increase in both sum columns.
+---------+--------+--------+
| derp.id | r1_sum | r2_sum |
+---------+--------+--------+
| 2 | 500.00 | 100.00 |
| 2 | 500.00 | 250.00 |
| 2 | 100.00 | 100.00 |
| 2 | 100.00 | 250.00 |
+---------+--------+--------+
r1_sum would incorrectly be 1200.00
r2_sum would incorrectly be 700.00
Question, is there a route other than custom SQL?
I can write the query myself easy enough, but if anyone has a suggestion which would avoid the writing of custom SQL that would be awesome.
Thanks for the help.
Edit: Here is a link to the annotations section of the Django documentation. One commenter mentioned the distinct option. This does not work, and I believe it is what is warned about at the bottom of the annotation section in the django documentation on annotation.
Edit2: The raw SQL idea likely is more difficult than I thought as derp.objects.raw('sql here') does not return the queryset object necessary for the admin to use it. Is there a way to use two queries (the real queryset plus a custom one doing the sums) and populate the listview from both? One suggestion I found (which I cannot find again now :S) suggested creating a view that maps to a Model definition which is then set to unmanaged by django (for syncdb). I could then write my custom code, and reference it for inclusion in the original query. This sounds messy. Thoughts?
If you want to stay within Django's queryset, I would consider creating a model superclass that shares the related and common fields and sub-classing for further distinctions. Otherwise, you need to either write custom SQL or get out of the database ORM entirely and manipulate your data in python with Queryset.values or Queryset.values_list
The best way I found to return the correct results was by using queryset.extra().
derp_r1_sum_select = """
select sum(`derp_related_1`.`amount`)
from `derp_related_1`
where `derp_related_1`.`fk` = `derp`.`pk`
"""
derp_r2_sum_select = """
select sum(`derp_related_2`.`amount`)
from `derp_related_2`
where `derp_related_2`.`fk` = `derp`.`pk`"
"""
def queryset(self, request):
qs = super(DerpAdmin, self).queryset(request)
qs = qs.extra(select={'derp_r1_sum': derp_r1_sum_select,
'derp_r2_sum': derp_r2_sum_select})
return qs