Denormalisation Magic, Round Two

31 views
Skip to first unread message

Andrew Godwin

unread,
Oct 26, 2008, 7:05:34 PM10/26/08
to Django developers
So, after the general, soft consensus of
http://groups.google.com/group/django-developers/browse_thread/thread/9a672d5bbbe67562/248e53722acab49e,
I've put my money (or should that be time?) where my mouth is and
actually coded my suggested solution.

The current diff against trunk is attached - the code will, I believe,
work as a standalone module anyway, but if people like this solution I
suggest a twiddle to db/models/base.py to add one more signal call to
make the code a bit nicer, as well as the obvious docs/tests, and
tweaking the coding style, etc.

Still, before I go for anything like that, I'd prefer people to test it
and tell me if I'm mad[1], sad, or if it's actually worth anything. With
the attached diff applied, you just write models like this:


class User(models.Model):
username = models.CharField(max_length=255)
email = models.TextField()
num_photos = models.AggregateField("Photo", "fk:user", "count")
num_all_photos = models.AggregateField("Photo", "all:", "count")

def __unicode__(self):
return "%s <%s>" % (self.username, self.email)

class Photo(models.Model):
caption = models.TextField()
user = models.ForeignKey(User, related_name="photos")
user_username = models.MirrorField("user", "username")

def __unicode__(self):
return "%s by %s" % (self.caption, self.user_username)


Apart from the lack of any actual photos on Photo objects, you get the
idea. Those remembering or reading the early discussion may remember my
original proposal for AggregateField had (queryset, column, function) -
due to various problems, like not being able to create querysets
referring to the right things or "self" (the instance), it's changed to
(model, link, function), where link is "all:" (which means 'run the
function over all members of the other model') or "fk:foo" (which means
'run the function over all members of the other model where the foo FK
points to this instance'). Obviously more link types could be added, but
I can't think of any other useful types yet.

As well as using built-in function names like "count", you're allowed to
pass in custom functions that take a queryset and a column name, and
return a result - when you use these, though, you have to also specify
the column type of the resulting column. Docs for all this will be
written in time.

So, what does everyone think? Useful, or just too much of an edge case?
Reasonable patch, or badly implemented?

Andrew

[1]: As an example of the insanity of some of this code, at one point it
changes the inheritance tree of its own subclass at runtime (line 86).
Better solutions to inheriting from an unknown-at-creation field type on
a postcard, please.

denorm.diff

Russell Keith-Magee

unread,
Oct 27, 2008, 6:43:12 AM10/27/08
to django-d...@googlegroups.com
On Mon, Oct 27, 2008 at 8:05 AM, Andrew Godwin <and...@aeracode.org> wrote:
>
> class User(models.Model):
> username = models.CharField(max_length=255)
> email = models.TextField()
> num_photos = models.AggregateField("Photo", "fk:user", "count")
> num_all_photos = models.AggregateField("Photo", "all:", "count")
>
> def __unicode__(self):
> return "%s <%s>" % (self.username, self.email)
>
> class Photo(models.Model):
> caption = models.TextField()
> user = models.ForeignKey(User, related_name="photos")
> user_username = models.MirrorField("user", "username")
>
> def __unicode__(self):
> return "%s by %s" % (self.caption, self.user_username)
>
...

> So, what does everyone think? Useful, or just too much of an edge case?
> Reasonable patch, or badly implemented?

First off, details and implementation notwithstanding, I'm +1 for
adding denormalization fields of this sort. Apologies for not getting
involved during the first pass of this discussion.

I do have a slight reservation regarding the API you are proposing.
The fields you have proposed (MirrorField and AggregateField) capture
the obvious use cases, but the syntax don't feel consistent with the
rest of the Django API. In particular, AggregateField seems to be
introducing a whole new query syntax that is completely independent of
the aggregation syntax that is due for inclusion in v1.1.

I would prefer to see a syntax that demonstrated slightly better
integration with the Django query syntax. If it isn't possible to use
QuerySet itself as a way to develop the underlying query, then I would
expect to see a syntax that at the very least retained the flavour of
the Django query syntax rather than inventing "fk:" style operators
that have no real analog.

MirrorField is essentially a query that returns a related object;
AggregateField is a query that returns an aggregate of related
objects. Looking longer term, this sort of approach also has the
potential to be integrated into database views, since defining a view
requires similar querying capabilities.

Unfortunately, I haven't given this enough thought to be able to
suggest an alternate syntax that will do the job. At this point, I
just know that I would rather avoid introducing 2 different syntaxes
for aggregates. I have a hazy vision in my head of the sort of thing
that I think could work; I'll try to get it to coalesce a little and
get back to you.

Yours,
Russ Magee %-)

Andrew Godwin

unread,
Oct 27, 2008, 8:26:01 AM10/27/08
to django-d...@googlegroups.com
Russell Keith-Magee wrote:
> I do have a slight reservation regarding the API you are proposing.
> The fields you have proposed (MirrorField and AggregateField) capture
> the obvious use cases, but the syntax don't feel consistent with the
> rest of the Django API. In particular, AggregateField seems to be
> introducing a whole new query syntax that is completely independent of
> the aggregation syntax that is due for inclusion in v1.1.
>
> I would prefer to see a syntax that demonstrated slightly better
> integration with the Django query syntax. If it isn't possible to use
> QuerySet itself as a way to develop the underlying query, then I would
> expect to see a syntax that at the very least retained the flavour of
> the Django query syntax rather than inventing "fk:" style operators
> that have no real analog.
>

Indeed; I was far happier with the queryset syntax for AggregateField I
originally proposed, but I couldn't get it to work - it just won't go
inside class bodies without far, far too much hackery. The MirrorField
API I like more; I have an alternate version where you simply pass it
the model name and column name, but that then has issues if you have
more than one FK to the same model.

The AggregateField proposal is just that, and I really squirmed at the
idea of "all:" and "fk:", but it got it working pretty quickly. One
possibility for more clean integration is an approach more like
querysets, so you could have something like

photos_count = models.AggregateField(Photo.view.filter(user_self=True))

The main problem with anything like this (and indeed with my attempt to
implement this with plain old querysets) is that, as a field, you exist
only on a class, and so when you get a signal saying a model has been
updated, it's somewhat difficult to determine which instances of
yourself to update - you can't loop through them all testing, since
that's hilariously inefficient. That's why I limited the querying to
only fk: and all: types, since detecting these is far more efficient.

I'd love to see any proposal for how Aggregates should look, as my
current incarnation really isn't too django-ish. I personally like
MirrorFields (now on their third name, more welcomed) as I have them
now, but then I would, since I invented them for my own use...

Andrew

A Mele

unread,
Nov 9, 2008, 10:16:35 AM11/9/08
to Django developers
I have followed the discussions about denormalization and I really
would like to see this in trunk. I agree with Rusell, the syntax
should be more like Django's query syntax, but I think this kind of
field is very useful for a lot of projects.


A Mele

Andrew Godwin

unread,
Nov 10, 2008, 11:08:51 AM11/10/08
to django-d...@googlegroups.com
A Mele wrote:
> I have followed the discussions about denormalization and I really
> would like to see this in trunk. I agree with Rusell, the syntax
> should be more like Django's query syntax, but I think this kind of
> field is very useful for a lot of projects.
>

I obviously agree, but Russ' point about it being ugly still stands, and
the only improvement I've been able to think of so far is changing
"fk:foo" into fk="foo", so you get something like:

models.AggregateField("Person", fk="nation", function="count")

i.e. the same call, but different syntax.

I might just go open a ticket for this soon anyway to get it recorded
properly, and at the very least release the code as a standalone module
- it's written so it can work like that anyway.

Andrew

Sebastian Bauer

unread,
Nov 10, 2008, 2:49:34 PM11/10/08
to django-d...@googlegroups.com
Hello, i think login_required should check that user is not only
authenticated, but also if is active. What do you think about this change?

Sebastian

Waylan Limberg

unread,
Nov 10, 2008, 3:26:37 PM11/10/08
to django-d...@googlegroups.com
On Mon, Nov 10, 2008 at 2:49 PM, Sebastian Bauer <ad...@ugame.net.pl> wrote:
>
> Hello, i think login_required should check that user is not only
> authenticated, but also if is active. What do you think about this change?
>

I see two problems with this:

1. This current behavior is fully documented [1]. In relevant part,
the documentation says:

> This doesn't control whether or not the user can log
> in. Nothing in the authentication path checks the
> is_active flag, so if you want to reject a login based
> on is_active being False, it is up to you to check that
> in your own login view. However, permission checking
> using the methods like has_perm() does check this
> flag and will always return False for inactive users.

2. Many people have expected the current behavior for some time and
such a change would be backward incompatible.

Although, I do see that the documentation specific to the
login_required view does not specifically mention the behavior.
Perhaps a note there would be beneficial.

[1]: http://docs.djangoproject.com/en/dev/topics/auth/#api-reference


--
----
Waylan Limberg
way...@gmail.com

Collin Grady

unread,
Nov 10, 2008, 3:32:00 PM11/10/08
to django-d...@googlegroups.com
On Mon, Nov 10, 2008 at 11:49 AM, Sebastian Bauer <ad...@ugame.net.pl> wrote:
> Hello, i think login_required should check that user is not only
> authenticated, but also if is active. What do you think about this change?

I think you can write your own decorator to do that, and it would be a
backwards-incompatible change, so it isn't likely to happen :)

--
Collin Grady

Sebastian Bauer

unread,
Nov 10, 2008, 3:38:18 PM11/10/08
to django-d...@googlegroups.com
but we can do this "little" modification in v1.1

i think this change will allow site administrator to ban users through change is_active flag

Waylan Limberg pisze:

John-Scott Atlakson

unread,
Nov 10, 2008, 4:57:25 PM11/10/08
to django-d...@googlegroups.com
On Mon, Nov 10, 2008 at 3:38 PM, Sebastian Bauer <ad...@ugame.net.pl> wrote:
but we can do this "little" modification in v1.1

i think this change will allow site administrator to ban users through change is_active flag

Waylan Limberg pisze:
On Mon, Nov 10, 2008 at 2:49 PM, Sebastian Bauer <ad...@ugame.net.pl> wrote:
  
Hello, i think login_required should check that user is not only
authenticated, but also if is active. What do you think about this change?

    
I see two problems with this:

1. This current behavior is fully documented [1]. In relevant part,
the documentation says:

  
This doesn't control whether or not the user can log
in. Nothing in the authentication path checks the
is_active flag, so if you want to reject a login based
on is_active being False, it is up to you to check that
in your own login view. However, permission checking
using the methods like has_perm() does check this
flag and will always return False for inactive users.
    

"Fully documented" as of one month ago [1]. This field was previously 'fully documented' as [2]:
"Boolean. Designates whether this account can be used to log in. Set this flag to ``False`` instead of deleting accounts."

Before these changes, the original help_text for the User.is_active field was: "Designates whether this user can login to the Django admin. Unselect this instead of deleting accounts."

I had brought this disconnect between the new documentation and behavior up in a previous thread [3] but failed to follow up on the discussion at that time. My point then was that:
1) django.contrib.auth.forms.
AuthenticationForm raises a ValidationError if user.is_active = False (thus preventing login via the django.contrib.auth.views.login view)
2) staff_member_required decorator only allows login if user.is_active = True

I now see that the test client also can only login when the credentials are for user.is_active = True and the django.contrib.auth.handlers.modpython.authenhandler also enforces this check.

In the previous thread, Malcolm said he wasn't trying to document the 'edge-cases' I brought up but now it's not so clear that these are really edge cases. This field appears to only be ignored by the `login_required` decorator and the django.contrib.auth.authenticate function for manually authenticating in views but seems to be respected everywhere else.

Malcolm said to report a documentation bug if the documentation is incorrect. Whenever I come to a different conclusion than a core dev, my first assumption is that I'm missing something since I really doubt I have anything close to the grasp a core dev has of the nitty gritty details of the code base. In this case, as best as I can tell, this new 'fully documented' behavior doesn't seem to jive with the code and previous documentation. I understand the worries about backwards compatibility, but the behavior and documentation seems to be inconsistent in its current state. It's not my call whether to make the documentation consistent with the code or vice versa, but I did want to bring up these complicating factors.

Cheers,
John-Scott

[1] http://code.djangoproject.com/changeset/9176
[2] http://code.djangoproject.com/browser/django/trunk/docs/topics/auth.txt?rev=8843#L90
[3] http://groups.google.com/group/django-developers/browse_thread/thread/3f46f4834b2bf9bf/cd26e079fa265497

David Cramer

unread,
Nov 10, 2008, 8:34:55 PM11/10/08
to Django developers
I'm not sure on AggreateField either. What if you just do like
("Photo", "user__exact=self.user") or something. Currently there's no
rerepsentation for "self" in Django QuerySet's, so this is a hard
thing to call. Also, we need a way to support callables.

e.g. models.MirrorField("my_callable_or_attribute") as well as the
models.MirrorFIeld("my_attribute", "its_subattribute")

Andrew Godwin

unread,
Nov 13, 2008, 7:13:38 AM11/13/08
to django-d...@googlegroups.com
David Cramer wrote:
> I'm not sure on AggreateField either. What if you just do like
> ("Photo", "user__exact=self.user") or something. Currently there's no
> rerepsentation for "self" in Django QuerySet's, so this is a hard
> thing to call. Also, we need a way to support callables.
>
> e.g. models.MirrorField("my_callable_or_attribute") as well as the
> models.MirrorFIeld("my_attribute", "its_subattribute")
>

Well, callables are a lot harder to deal with; there's no efficient way
to hook onto when they've changed. It would be possible if you passed
along a foreign key to watch for changes, although at that point you
could probably also implement it using an AggregateField of some kind,
since they already take arbitary aggregate functions.

The problem is the 'self' thing, though. However, I like the
filter()-style syntax, I'll need to see if that's easy enough to roll in
so it still makes sense...

Andrew

init...@googlemail.com

unread,
Nov 16, 2008, 4:38:12 PM11/16/08
to Django developers
i just implemented something like this. it useses decorator-syntax
to create a denormalized field from a callable.
i think the advantages of this aproach are:
1) it's very flexible
2) all code related to the denormalisiation is located in one central
place,
and not one place for the field definition and another place for the
actual code

i also implemented a management command for rebuilding all the
denormalisations.
a (working) example:
http://github.com/initcrash/django-denorm/tree/master/example/gallery/models.py

the actual implementation of the decorator contains quite a few ugly
hacks, maybe
this can be done in a cleaner way by changing some code inside django
itself.
http://github.com/initcrash/django-denorm

Andrew Godwin

unread,
Nov 23, 2008, 6:00:09 AM11/23/08
to django-d...@googlegroups.com
Hmm, now that's an interesting implementation. I'm pleased to see you've
essentially taken the same route I did in terms of detecting when to
save, which means I'm not being as silly as I first thought.

The decorator syntax is nice, and certainly something that should be
around - however, I (naturally) feel that the most common cases - i.e.
copying values across, and count() queries - should have an actual field
people can use, to lower the barrier to entry.

Additionally, your code loops over every model every time, whereas in
some cases you can detect which specific model needs updating (for
example, if you know that the field "planet" on the just-saved object is
a foreign key to your model).

Still, I certainly like the idea of having a decorator syntax; I'm going
to go away and see if I can persuade my AggregateField to turn
inside-out like that.

Andrew

Christian Schilling

unread,
Nov 24, 2008, 5:08:24 PM11/24/08
to Django developers
I think it would be even nicer if the function passed into the
decorator could
actualy return the new value of the field, instead of assigning it and
call save().
Currenty the function does two tasks:
1) figure out what needs to be updated "resolve dependenys"
2) calculate the new value
maybe the first task could be implemented in such a generic way, that
it fits
most cases.
Maybe something like a DependencyResolver class that has a method
witch takes
an instance (the one beeing saved) and returns a queryset (instances
that need to be
updated.
this could result in a syntax like this:

@denormalized(models.TextField,blank=True,depend=
['self',DirectForeignKey(Picture)])
def users():
return ', '.join(str(p.owner) for p in self.picture_set.all())

so DirectForeignKey (has DependencyResolver as base class) could look
for a FK to Gallery in Picture.
this way the most common cases would require very litte code, and if
somebody has
some very uncommon dependencies he can implement a special resolver.
i'll some experimenting on implementing this.

Christian Schilling

unread,
Nov 24, 2008, 7:03:26 PM11/24/08
to Django developers
one more thing...

On Nov 23, 12:00 pm, Andrew Godwin <and...@aeracode.org> wrote:
> Additionally, your code loops over every model every time, whereas in
> some cases you can detect which specific model needs updating (for
> example, if you know that the field "planet" on the just-saved object is
> a foreign key to your model).

i don't think so.
if you mean lines 35-38 in fields.py:
this is only used to rebuild all denormalized values in the whole DB
via the management command, witch means everything needs to be updated.

Andrew Godwin

unread,
Nov 25, 2008, 5:22:48 AM11/25/08
to django-d...@googlegroups.com
Christian Schilling wrote:
> i don't think so.
> if you mean lines 35-38 in fields.py:
> this is only used to rebuild all denormalized values in the whole DB
> via the management command, witch means everything needs to be updated.
>

Ah yes, ignore me. I was trying to see how you did 'efficient updates'
(where, say, only half of a model's instances are affected by the change
that just occurred) but I now see it's the function's job to do that.

I quite like this solution, it's rather clean; my own solution is far
more lengthly, and while some of that is the efficiency code I
previously mentioned, I can't help but think I have some redundancy in
there now...

I've published my code at http://code.google.com/p/django-denorm/, and
I'll 'release' it soonish, but I'm liking this decorator approach more
and more, especially as we can then just build 'AggregateFields' on top
of them.

Andrew

Christian Schilling

unread,
Nov 25, 2008, 12:18:10 PM11/25/08
to Django developers
i spend some time implementing my idea above (still just a proof of
concept, all testing i did was on the example project)
the resulting models.py: (still in the same place)
http://github.com/initcrash/django-denorm/tree/master/example%2Fgallery%2Fmodels.py

as you can see, this makes the @denormalized decorator a drop-in
replacement for pythons @property
decorator. and the function becomes much shorter and easier to
understand.

Andrew Godwin

unread,
Nov 28, 2008, 11:30:27 AM11/28/08
to django-d...@googlegroups.com
So, I've had a good look over the (constantly-growing!) code here, and
I'm really beginning to like the decorator approach, especially the nice
way you combine dependencies with the dependency classes, which makes my
solution to old/new FK values look terrible.

I still have two issues, although I'm pretty sure both can be fixed.

One: This is the MirrorField equivalent in the example:

@denormalized(models.CharField,max_length=100)
@depend_on_related(User)
def owner_username(self):
return self.owner.username

While it's really obvious what it's doing, I (for obvious reasons) would
like to have common use cases like that shortened, and the number of
things I have to type reduced (I only need one of the other model or the
foreign key attribute name, and here I need both). I've tried writing a
wrapper for MirrorField, but I'm running into the same issue I had
(which is that you have to do it on the class_prepared signal). I'll
probably have it working soon, though.

The second thing is that it doesn't play nice with South, but that's
almost certainly my fault, and nothing to do with you. If we hadn't
started scanning source code for the field definitions and ripping them
out it might have been fine, but it's proved to be the only real way...

Basically, I'm moaning as it doesn't work with my peculiar setup :)
Still, 'tis nice overall. I have a partially-complete unit test suite
for my denorm code I'd be happy to port over (it has the wonderful set
of tricks you need to unit test with a whole fake app, while still
keeping database settings) if you want.

Andrew

Christian Schilling

unread,
Nov 29, 2008, 11:12:05 AM11/29/08
to Django developers


On Nov 28, 5:30 pm, Andrew Godwin <and...@aeracode.org> wrote:
>
> One: This is the MirrorField equivalent in the example:
>
>     @denormalized(models.CharField,max_length=100)
>     @depend_on_related(User)
>     def owner_username(self):
>         return self.owner.username
>
> While it's really obvious what it's doing, I (for obvious reasons) would
> like to have common use cases like that shortened, and the number of
> things I have to type reduced (I only need one of the other model or the
> foreign key attribute name, and here I need both). I've tried writing a
> wrapper for MirrorField, but I'm running into the same issue I had
> (which is that you have to do it on the class_prepared signal). I'll
> probably have it working soon, though.
I think the problem here is that you need to alter a model after
django has initialized it, because there is just no other way to auto-
detect
the field-type of the mirrored field. So eliminating the need to type
both the related model name as well as the field name is not hard at
all,
syntax could look like:

owner_username = denormalized(models.CharField,max_length=100)(Mirror
('owner','username'))

so Mirror instances would be a callable with the proper dependency
attached to it.
Eliminating the need for the first line, where you specify the field
type
might be impossible to do in a clean way.

> The second thing is that it doesn't play nice with South, but that's
> almost certainly my fault, and nothing to do with you. If we hadn't
> started scanning source code for the field definitions and ripping them
> out it might have been fine, but it's proved to be the only real way...
This is relevant for me to, as i use south for my migrations.

> Still, 'tis nice overall. I have a partially-complete unit test suite
> for my denorm code I'd be happy to port over (it has the wonderful set
> of tricks you need to unit test with a whole fake app, while still
> keeping database settings) if you want.

This would be great, my current test coverage is rather small and
better/more
tests will cerainly be exremly usefull when trying to optimise the
code.

Andrew Godwin

unread,
Nov 29, 2008, 6:54:24 PM11/29/08
to django-d...@googlegroups.com
Christian Schilling wrote:
> I think the problem here is that you need to alter a model after
> django has initialized it, because there is just no other way to auto-
> detect
> the field-type of the mirrored field. So eliminating the need to type
> both the related model name as well as the field name is not hard at
> all,
> syntax could look like:
>
> owner_username = denormalized(models.CharField,max_length=100)(Mirror
> ('owner','username'))
>
> so Mirror instances would be a callable with the proper dependency
> attached to it.
> Eliminating the need for the first line, where you specify the field
> type
> might be impossible to do in a clean way.
>

Yeah, it pretty much is. I did it in my original code, but only by
hooking into class_prepared to then traverse across the foreign key, and
_then_ dynamically reparent the field class to the right parent. It's
python, so it's possible, but that doesn't mean it's sensible. Besides,
I'm not sure all Python implementations like you fiddling with __bases__.

> This is relevant for me to, as i use south for my migrations.
>

Ooh, excellent :) I've fixed this, anyway; South has provision for more
magical fields, and if they have a south_field_definition function will
use that instead of source file scanning, so one more method on the
DBField subclass and it's playing very nicely. I am now happy.

> This would be great, my current test coverage is rather small and
> better/more
> tests will cerainly be exremly usefull when trying to optimise the
> code.
>

The start of this and the South stuff is over on my branch:

http://github.com/andrewgodwin/django-denorm/tree/master

The unit test 'suite' is hardly finished yet (one test...), but it does
at least do a pretty thorough use check of some of the more basic
scenarios. ./manage.py test denorm does indeed test and come out OK,
though - it even brought up an infinite recursion bug I also had in my
code, which I've fixed (there may be a cleaner fix).

Andrew


Christian Schilling

unread,
Nov 30, 2008, 3:37:55 PM11/30/08
to Django developers
On Nov 30, 12:54 am, Andrew Godwin <and...@aeracode.org> wrote:
> The unit test 'suite' is hardly finished yet (one test...), but it does
> at least do a pretty thorough use check of some of the more basic
> scenarios. ./manage.py test denorm does indeed test and come out OK,
> though - it even brought up an infinite recursion bug I also had in my
> code, which I've fixed (there may be a cleaner fix).
>

yes, i merged that, ran my old tests against the fixed version and
found a bug in the fix..
so, i recreated the scenario in the new testsuite and fixed the
fix ;-)

i think we should really setup a trac instance somewhere...

Andrew Godwin

unread,
Nov 30, 2008, 3:55:52 PM11/30/08
to django-d...@googlegroups.com
Christian Schilling wrote:
> yes, i merged that, ran my old tests against the fixed version and
> found a bug in the fix..
> so, i recreated the scenario in the new testsuite and fixed the
> fix ;-)
>

Ah, the wonderful meta-loop of fix fixes.

> i think we should really setup a trac instance somewhere...

I have a Trac setup around on my server for South, so if you want I'll
kick that slightly and install the Git plugin...

Andrew


Christian Schilling

unread,
Nov 30, 2008, 5:33:50 PM11/30/08
to Django developers
On Nov 30, 9:55 pm, Andrew Godwin <and...@aeracode.org> wrote:
> I have a Trac setup around on my server for South, so if you want I'll
> kick that slightly and install the Git plugin...

this should be easier than setting it up on my server where i run
debian/stable which contains only an old version of trac and no trac-
git at all.

Ben Welsh

unread,
May 16, 2009, 8:29:47 PM5/16/09
to django-d...@googlegroups.com
Just want to step out of lurker mode and pipe up with my modest +1.

I've been able to hack together simple denorm tricks for FK fields and static data that work something like these fields -- but one thing I could really use some help with yet is pulling off denormalization of frequently changing M2M fields. Do y'all think that's something that will have to wait for M2m signals like ticket 5390 or can modifications to the methods here make it work in 1.0? I haven't been able to square the circle in my feeble brain, so here I am.

Love what you're doing,

Ben.
Reply all
Reply to author
Forward
0 new messages