Okay, so I've uploaded the code for the serializer so you can take a look: https://gist.github.com/9167eff88d787b3b5279
Disclaimer: Again, I freely admit that this code is extremely messy and may well contain a few bugs (I don't have any test cases for it). It isn't something I use on a production server; we just use it to load test data on our dev machines. But, the idea is there and it does serialize/deserialize (for what we use it for) well. It's pretty slow on import due to all the queries it's making, and I'm sure you can improve on that =) On 30 Mar, 00:33, Russ Amos <taal...@gmail.com> wrote: > Oliver, examining unique and unique_together attributes is exactly what I > have in mind for attempting to follow relationships when deserializing. I > realize it needs to be dealt with carefully in the inner workings of Django, > to provide consistency, but anything you're willing to provide, I'm willing > to read through and maybe refactor. > > So, yeah, I'd like to take a look at what you've written. Pragmatism may not > make for the cleanest code, but I think it's a great way to find "gotchas" > that might otherwise be difficult to unearth. > > Russ > > > > On Sun, Mar 29, 2009 at 6:23 PM, Oliver Beattie <oli...@obeattie.com> wrote: > > > I'll admit that I haven't read your whole post (sorry), but one part > > caught my eye… the bit about storing relationships not just as primary > > keys. If I am right in thinking that you are wanting to do some sort > > of "relationship following" I think I can probably help by providing > > some initial (but a bit messy) code. I've recently run into the issue > > of primary-key-dependant fixtures breaking in interesting ways on a > > real database, so had to implement the relationship following (I have > > a serializer I've named jsonfk). Basically, it will look at unique and > > unique_together fields in models and figure out what it can use, > > falling back to the primary key if either no unique fields exist or > > the lookup fails on deserialization. > > > Sorry if I'm rambing a bit… it's late, but let me know if this is > > something that may be of interest to see. It's not something I want to > > release publicly since it's a bit of a mess and doesn't handle M2M > > relations (on the project I've used it on M2M relationships aren't > > really used). > > > On 29 Mar, 22:57, Russ <taal...@gmail.com> wrote: > > > My apologies for the length! > > > > Concisely, I intend to provide the Django user with some granular > > > control of the data to be serialized without sacrificing backwards > > > compatibility for old code, or for users who need the straightforward, > > > current functionality. > > > > Any serialized Model contains only the data in the source row of the > > > database table. Regarding inheritance, this is only an issue when the > > > model in question derives from one or more concrete models. In that > > > case, however, any data contained on the far end of the one-to-one > > > relation is not serialized at all. The overarching issue is rooted in > > > the wayDjango serializers treat relationships. This is clearly a > > > complex issue, because both shallow and deep serialization are useful > > > in a large range of situations. > > > > For the purpose of argument and demonstration, please examine the > > > following use case. A business wishes to begin providing their > > > products online, but they wish to continue using the inventory > > > management software with which they are familiar > > > (henceforth,ezInventory). The models.py looks like: > > > > from django.db import models > > > > class Product(models.Model): > > > name = models.CharField(max_length=200) > > > description = models.TextField() > > > # We're assuming all prices are in USD... > > > price = models.DecimalField(max_digits=6, decimal_places=2) > > > > class Order(models.Model): > > > products = models.ManyToManyField(Product) > > > order_placed = models.DateTimeField() > > > > def total_price(self): > > > return self.products.aggregate(models.Sum('price')) > > > > Django's serialization facilities make importing and exporting > > > products and orders between their website and ezInventory a breeze. > > > Six months later, however, they have a feature request. > > > > They would like to begin providing support for some of their products > > > via subscription. This is an easy addition: > > > > # imports... > > > # original models... > > > > class Subscription(Product): > > > recurrence = models.PositiveIntegerField(choices=((12, 'annual'), > > > (1, 'monthly'))) > > > > Immediately, a problem is evident: the business relies on easy > > > communication of data between ezInventory and the website, but Django > > > serializes the class Subscription as follows: > > > > [{ > > > "pk": 13, > > > "model": "app.subscription", > > > "fields": { > > > "recurrence": 12 > > > } > > > > }] > > > > Now, this is wonderful, as long as we know to pair this information > > > with the product with id 13. However, ezInventory, though supportive > > > of subscriptions in general, merely overwrites the product with id 13 > > > (assuming the products table was imported first) and complains about > > > the missing fields. > > > > Skipping merrily back to the real world, here is the issue, plain and > > > simple. Sometimes, you need to include more information than is just > > > in the one model. Sometimes, that's going to appear in the form of > > > inherited models, sometimes via one-to-many or many-to-many > > > relationships, and sometimes just little relevant bits of information > > > that may not actually be in the model at all. There are numerous open > > > tickets about this issue [1][2][3][4]. Clearly, this needs some > > > thought. > > > > I like the idea [5] of providing a Serializer class, defined similarly > > > to the ModelAdmin class, to allow custom ways of serializing data, > > > something like: > > > > # in serializers.py (or something) > > > class ProductListing(serializers.Serializer): > > > fields = { > > > 'Product': ['price', 'name', 'description'], > > > 'Subscription': ['price', 'name', 'description', 'recurrence'] > > > } > > > > serializers.register(ProductListing) > > > > $ python manage.py shell>>> from project.app import models > > > >>> from django.core import serializers > > > >>> print serializers.serialize('json', > > list(models.Product.objects.all()) + > > > > ... list(models.Subscription.objects.all()), > > > ... serializer='ProductListing', indent=4) > > > [{ > > > "pk": 1, > > > "model": "app.product", > > > "fields": { > > > "price": 9.98, > > > "name": "Product #1", > > > "description": "Description for Product #1" > > > }}, > > > > # other products... > > > { > > > "pk": 13, > > > "model": "app.subscription", > > > "fields": { > > > "price": 14.98, > > > "name": "Subscription #1, Product #13", > > > "description: "My demonstrations aren't particularly > > > creative.", > > > "recurrence": 12 > > > } > > > > }] > > > > Not only is this backwards-compatible (no new serializers == no new > > > behavior), but it also continues to allow deserialization: The > > > deserialization process would see that app.subscription is a child of > > > product, and fill in the data appropriately. This circumvents one of > > > the largest drawbacks, IMO, ofDjangoFullSerializers [6]; being able to > > > deserialize this data is often just as important as serializing it > > > (lookin ' at you, fixtures). The observant reader will note that, as > > > thus demonstrated, it only solves this issue for models using > > > inheritance, not those using one-to-many or many-to-many fields. So, > > > let's serialize some orders. > > > > # in serializers.py (or something) > > > class OrderSerializer(serializers.Serializer): > > > fields = { > > > # include values from the products, and the return value of > > > total_price > > > 'order': [{'products': ['price', 'name']}, 'total_price'] > > > } > > > > serializers.register(OrderSerializer) > > > > $ python manage.py shell>>> from project.app import models > > > >>> from django.core import serializers > > > >>> print serializers.serialize('json', models.Order.objects.get(pk=1), > > > > ... serializer='OrderSerializer', indent=4) > > > [{ > > > "pk": 1, > > > "model": "app.order", > > > "fields": { > > > "products": [{ > > > "pk": 1, > > > "model": "app.product", > > > "fields": { > > > "price": 9.98, > > > "name": "Product #1" > > > } > > > }, > > > { > > > "pk": 13, > > > "model": "app.subscription", > > > "fields": { > > > "price": 14.98, > > > "name": "Subscription #1, Product #13" > > > } > > > }], > > > "total_price": 14.96 > > > } > > > > }] > > > > As long as enough data is presented, the deserializer stands no less > > > chance of accurately connecting models together than in its current > > > form. Arguments could be provided to search for matching rows and > > > correct the primary keys from serialized data, or to update the fields > > > in the row of given primary key. > > > > A couple of notes. I think that providing 'excludes' behavior could > > > be done by prefixing a field name with a minus sign, i.e.: > > > > fields = {'order': ['-order_placed']} > > > > If the only fields listed are prefixed with a minus sign, we can > > > assume that all remaining fields are to be included. Mixing prefixed > > > and normal fields assumes implicit negations for any fields not > > > mentioned: > > > > fields = { > > > 'order': ['-order_placed', 'total_price'] > > > } > > > > serializes only the total_price pseudo-field, ignoring both > > > order_placed, explicitly, and products, implicitly. This is just > > > personal preference -- the decision needs to be made, and an 'exclude' > > > member to provide explicit field exclusions is just as sensible. > > > > Providing a member to allow shortcut fields definition is a good > > > idea. For example: > > > > class OrderSerializer(serializers.Serializer): > > > # similar to QuerySet.select_related('product')... > > > select_related = ['product'] > > > # or, to mirror QuerySet.select_related(depth=1)... > > > select_related = 1 > > > > This would use the default behavior, except the 'products' key in the > > > output > > ... > > read more » --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/django-developers?hl=en -~----------~----~----~----~------~----~------~--~---