[prev in list] [next in list] [prev in thread] [next in thread]
List: pyamf-commits
Subject: [pyamf-commits] r2929 - in pyamf/branches: . bytearray-context-695
From: SVN commit logs for PyAMF <commits () pyamf ! org>
Date: 2009-10-19 10:33:39
Message-ID: 20091019103339.8037B7C8017 () mail ! collab ! com
[Download RAW message or body]
Author: nick
Date: 2009-10-19 12:33:37 +0200 (Mon, 19 Oct 2009)
New Revision: 2929
Added:
pyamf/branches/bytearray-context-695/
pyamf/branches/bytearray-context-695/CHANGES.txt
pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py
pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py
Removed:
pyamf/branches/bytearray-context-695/CHANGES.txt
pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py
pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py
Log:
Branching to source:pyamf/branches/bytearray-context-695
Deleted: pyamf/branches/bytearray-context-695/CHANGES.txt
===================================================================
--- pyamf/trunk/CHANGES.txt 2009-10-10 21:51:34 UTC (rev 2927)
+++ pyamf/branches/bytearray-context-695/CHANGES.txt 2009-10-19 10:33:37 UTC (rev \
2929) @@ -1,345 +0,0 @@
-Changelog
-=========
-
-This file contains information about the changes between the different
-versions of PyAMF.
-
-.. contents::
-
-0.6 (unreleased)
-----------------
- - Fixed a small bug in decoding Django relations when declared as static.
- Thanks to wolver for the patch. (Ticket:693)
- - Removed ``exceptions`` from all ``Context`` and ``pyamf.util.Indexed*``
- classes (Ticket:660)
- - Empty Django relations are now encoded as ``None``, not ``pyamf.Undefined``
- (Ticket:659)
- - ``pyamf.ClientType`` removed, as it is not spec compliant (Ticket:651)
-
-0.5.1 (2009-09-19)
-------------------
- - pyamf.register_package can now accept a list of classes (Ticket:650)
- - Fixed a regression in TwistedGateway where services would be called twice
- (Ticket:648)
- - Fixed a bug with encoding anonymous trait references (Ticket:644)
- - Moved IndexedCollection into cpyamf (Ticket:424)
- - amf3.encode_int now encodes signed 29bit ints, not unsigned. Thanks to
- gerard for the report, investigation and patch! (Ticket:646)
-
-0.5 (2009-09-07)
-----------------
- - Added the ability to modify the timezones of dates being de/encoded for
- legacy systems. (Ticket:612)
- - Fixed a Django reference bug where parent.child.parent is parent would
- encode 3 objects and not 2. (Ticket:642)
- - Setting `None` on a Django DateField would blow up the encoder (Ticket:632)
- - `rootCause` in ErrorFault objects are now populated with the traceback
- object. (Ticket:637)
- - Support for encoding/decoding BlazeDS specific messages (Ticket:581)
- - Removed tracebacks from exception messages if debug=False (Ticket:552)
- - Support for google.appengine.ext.db.polymodel.PolyModel (Ticket:633)
- - Support for Django File/ImageField (Ticket:631)
- - Support for Django model inheritance (Ticket:626)
- - Rewrote attribute handling code. Added the ability to exclude, set
- read-only and static attributes. See http://pyamf.org/wiki/AttributeControl
- for more details. (Ticket:601)
- - Exposed the amf request object if expose_request=True.
- Twisted/Django/Google have an amf_request property on the http request
- object. WSGI has an entry in the environ dict - pyamf.request (Ticket:234)
- - Added support for the `array` module (Ticket:468)
- - Fixed an issue with Django model AutoFields being set to 0 by the Flex
- client (Ticket:556)
- - Added support for the collections module. (Ticket:474)
- - Added type checks for class objects. (Ticket:473)
- - GAE FloatProperty can now accept int's without issue (Ticket:609)
- - Revamped the lazy imports module to use sys.meta_path instead. Fixes all
- erroneous import errors (Ticket:485).
- - Django models will now accept dynamic properties (Ticket:575)
- - Django PK properties will be set first to allow related properties to apply
- correctly (Ticket:599)
- - p.r.encode/p.r.decode both have a new logger kwarg. Supply a logging.Logger
- instance if you want to see debug output (Ticket:588)
- - pyamf.TypedObjectClassAlias now forces the class type (Ticket:537)
- - If a call to Decoder.readElement results in an IOError exception, the
- original position of the stream will be restored (Ticket:573)
- - Replaced hardcoded Python implementation title in p.r.gateway.SERVER_NAME
- (Ticket:541)
- - Provide a user friendly way to handle datetime.time objects (Ticket:498)
- - Removed the useless pyamf.logging module (Ticket:577)
- - Added a new register_package helper function to make bulk class
- registration easier. Check the docstring for more info. (Ticket:576)
- - cpyamf.amf3._decode_int now propagates exceptions correctly (Ticket:506)
- - Fixed an incorrect OverflowError when encoding large integers in AMF3
- (Ticket:519)
- - Added `append` to pyamf.util.BufferedByteStream (Ticket:574)
- - Added Epydoc signatures to all util.StringIOProxy, util.DataTypeMixIn and
- util.BufferedByteStream classes (Ticket:440)
- - Removed dependancy on fpconst. Platforms with broken platforms are detected
- and handled correctly internally (Ticket:564)
- - amf0.Encoder now supports attribute ordering (Ticket:558)
- - Removed duplicate method Context.reset, in favour of Context.clear
- (Ticket:527)
- - Rewrote cpyamf.util.BufferedByteStream to not depend on cStringIO. Added a
- C based api for stream functions (Ticket:513)
- - Unified exceptions for BufferedByteStream so that only IOError is raised.
- (Ticket:520)
- - Made raising pyamf.ReferenceError optional for all Indexed* and Context
- classes. Refactored AMF3 so that only one ClassDefinition is created per
- class (Ticket:524)
- - Strict type checking now on pyamf.util.BufferedByteStream (Ticket:512)
- - Removed the default loggers from p.r.gateway.*. To re-enable supply a
- logger instance as a keyword arg to the gateway constructor (Ticket:525)
- - Removed pyamf.util.make_classic_instance (as it is not used) (Ticket:521)
- - Removed pyamf.util.get_mro, in favour of inspect.getmro (Ticket:526)
- - Removed pyamf.util.Indexed[Collection|Map].remove (Ticket:518)
-
-0.4.2 (2009-04-20)
-------------------
- - Support for decoding the 'source' property on ArrayCollections (Ticket:488)
- - Fixed an issue in the GAE adapter where dynamic properties would be missing
- on referenced objects. (Ticket:511)
- - Fixed a critical issue with AMF0 reference counting when encoding remoting
- responses. (Ticket:510)
- - Strengthened http header handling in the client (Ticket:492)
- - Support for Django i18n ugettext_lazy (Ticket:496)
- - Added support for microseconds for datetime objects. Thanks to Derek Payton
- for the patch. (Ticket:490)
- - Added support for property types on SQLAlchemy mapped classes. (Ticket:491)
- - Added support for property types for Google AppEngine db.Model and
- db.Expando. (Ticket:487)
-
-0.4.1 (2009-02-23)
-------------------
- - amf0.Encoder.use_amf3 has been extended to cover all object types
- (Ticket:453,467)
- - Property types on Django models will now encode as expected. (Ticket:480)
- - Django models.ForeignKey properties will be followed only if previously
- accessed outside of PyAMF. `_[attr]_cache` is no longer encoded.
- (Ticket:456)
- - Encoding {0:0, '0':1} will now raise an AttributeError. (Ticket:458)
- - Google AppEngine adapter improvements - see ticket for details (Ticket:479)
- - amf0.Encoder will encode all elements as AMF3 if use_amf3 option is set to
- True (Ticket:453)
- - Unicode handling in __repr__ functions has been improved (Ticket:455)
- - object attributes and dict keys are now utf8 encoded bytestrings. Python
- \**kwargs doesn't accept unicode key objects. (Ticket:463)
- - Django models.TimeField, models.DateField will now be converted to the
- correct type (datetime.time and datetime.date respectively).
- fields.NOT_PROVIDED is also checked for by converting to pyamf.Undefined
- and back again. (Ticket:457)
-
-0.4 (2009-01-18)
-----------------
- - cpyamf now deals with exceptional floats the same way as pure python -
- especially on windows (Ticket:448)
- - Support for SQLAlchemy 0.5.1 (Ticket:449)
- - amf0.Encoder now has a use_amf3 flag which determines which XML type to
- return to the client (Ticket:435)
- - BufferedByteStream.truncate(length) now actually does something useful
- (Ticket:444)
- - setup.py now gets the version number from pyamf/__init__.py source
- (Ticket:429)
-
-0.4rc3 (2009-01-14)
--------------------
- - Support for SQLAlchemy 0.5.0 (Ticket:436)
- - pyamf.util.DataTypeMixIn/cpyamf.util.BufferedByteStream can now
- encode/decode 24bit un/signed integers. (Ticket:422)
- - pyamf.util.StringIOProxy/cpyamf.util.BufferedByteStream both have new
- consume methods that will chop of the tail of the stream (already read
- stream). (Ticket:423)
- - Now checking for all types of supported xml lib types for encoding, but
- will only use the first implementation for decoding (Ticket:426)
- - fpconst dependancy is now only required if the platform requires it
- (Ticket:356)
- - Decoding negative timestamps on certain platforms (namely Windows) are now
- supported (Ticket:390)
-
-0.4rc2 (2009-01-05)
--------------------
- - Support for SQLAlchemy 0.4.0 (Ticket:410)
-
-0.4rc1 (2008-12-31)
--------------------
- - Support for encoding/decoding SQLAlchemy ORM objects with a new adapter.
- Lots of people involved in this one, but special thanks to Dave Thompson
- and Michael Van Tellingen for making this happen. (Ticket:277)
- - All gateways now log exceptions when exceptions are raised during
- en/decoding. (Ticket:394)
- - Flex messaging now uses correct static attributes for encoding. Determining
- static/dynamic attributes on objects is now easier (Ticket:357)
- - Added use_proxy option to amf3 which will automagically convert ObjectProxy
- to dict and ArrayCollection to list on decoding, and vice versa on encode.
- Thanks to dthompso for the excellent patch (Ticket:355)
- - flex.ArrayCollection now subclasses list instead of dict as non-int keys
- are not allowed. IList interface has been implemented. (Ticket:349)
- - Encoding registered subclass of list that define the 'externalised'
- metadata will be encoded as an object.
- - Encoders how have a 'strict' mode. Not generally useful for the time being
- but will help with developments in the future. Type mapped functions now
- require a second 'encoder' attribute. (Ticket:378)
- - Added adapter to handle the decimal module and, if strict is set to False,
- silently converts a Decimal instance to a float (Ticket:376)
- - ClassAlias can now be subclassed and three new methods have been added:
- applyAttributes, createInstance, getAttributes all which help to provide
- fine control for object/instance manipulation (Ticket:348)
- - Added support for __slots__ (Ticket:347)
- - Fixed problem when decoding objects that map to GAE db.Model objects with
- required properties (Ticket:342)
- - ByteArray now does not throw an error when used in the Remoting framework
- (Ticket:379)
- - A new adapter that converts sets.ImmutableSet and sets.Set to tuples before
- encoding. (Ticket:280)
- - A revamped google app engine adapter that checks for the _key attribute in
- an aliased class and first loads the object from the datastore and then
- applies that properties in the object stream. (Ticket:307)
- - New helper function to make it easier to manually add adapters (Ticket:350)
- - Ability to disable the c extension with passing --disable-ext to setup.py
- (Ticket:381, 391)
- - Python C-extension for the pyamf.util.BufferedByteStream class. Originally
- contributed by Gerard Escalante (Ticket:225, 405)
- - New API to add headers such as cookies in pyamf.remoting.client
- (Ticket:337)
- - Now clearing the context between remoting requests (Ticket:309)
- - Fixed issue with AMF3 class definition references (Ticket:341)
- - More helpful description for register_class args check (Ticket:334)
- - pyamf.register_class now checks to ensure that __init__ args do not have
- any arguments (Ticket:322)
- - Added RemoteObject support for AsyncMessage (Ticket:292)
- - pyamf.remoting.ErrorFault.__repr__ now displays the traceback info (if it
- exists). (Ticket:331)
- - Both Encoders will now raise pyamf.EncodeError if a function is encoded
- (Ticket:311)
- - Twisted Gateway would fall over when returning tuples (Ticket:313)
- - The remoting gateways now send a customizable Server header (Ticket:317)
- - The remoting client now sends a customizable User-Agent header (Ticket:306)
- - Added ability to set the HTTP referer in remoting client (Ticket:316)
- - Fixed issue where the AMF3 encoder assumed objects with a 'tag' attribute
- needed XML encoding. Reported by cy-man (Ticket:303)
- - Solved issue with repr for AbstractMessage. Reported by datafunk
- (Ticket:283)
- - Content-type was missing in POST requests from the AMF client. Reported by
- magog (Ticket:304)
- - Added the disconnect Command operation (Ticket:325)
- - Fixed issue with the unit tests for Django (Ticket:281)
- - Removed the NetworkIOMixIn class (Ticket:232)
-
-0.3.1 (2008-05-04)
-------------------
- - Importing module now has tests (Ticket:266)
- - Django model adapter now imported only when django.db.models is imported
- (Ticket:261)
- - Google Model/Expando encoding now works out of the box
- - Fixed issue with Remote Object destination (Ticket:270)
- - Added a new gateway for the Google App Engine - see
- pyamf.remoting.gateway.google.WebAppGateway (Ticket:253)
- - amf0 Encoder now takes amf3 contexts into account (Ticket:268)
- - amf*.encode helpers can now accept multiple arguments (Ticket:267)
- - Removed the dependancy of fpconst for Python 2.5 or newer (Ticket:243)
- - Solved issue with AMFPHP exceptions in AMF client (Ticket:258)
- - Fixed issue with url parsing in AMF client (Ticket:256)
- - Client no longer raises httplib.ResponseNotReady when making multiple
- requests using the same RemotingService (Ticket:254)
-
-0.3 (2008-04-14)
-----------------
- - Added compatibility module for Google App Engine (Ticket:247)
- - Fixed the signed interpretation of compressed integers in AMF3 (Ticket:241)
- - Classic class decoding would throw an AttributeError (Ticket:248)
- - Reloading adapter modules caused errors in Django, Pylons and Google App
- Engine. Resolved by removing dependancy on Importing module and
- incorporating into pyamf.util (Ticket:250)
- - Adapter framework can now be fired when only loading submodules
- (Ticket:246)
- - Made util.BufferedByteStream endian aware (Ticket:231)
- - Fixed issue with Twisted threads (Ticket:233)
-
-0.2 (2008-03-12)
-----------------
- - Removed amfinfo console_script (Ticket:226)
- - Encoders/Decoders now check for __getstate__/__setstate__ respectively
- (Ticket:209)
- - Gateway import hack has now been removed - permanently (Ticket:224)
- - Encoding/decoding performance has been increased 2x for AMF0 and up-to
- 10x(!) for AMF3 (Ticket:198)
- - Logging is now possible in all the supported gateways (Ticket:173)
- - A new preprocessor function that runs after authentication, but before
- invoking the service method (Ticket:196)
- - authenticator can now be decorated with expose_request (Ticket:195)
- - Python 2.3 support (Ticket:33)
- - Python 2.6 support (Ticket:222)
- - Made PyAMF distributable as zip-based Python Egg (Ticket:193)
-
-0.1.1 (2008-02-18)
-------------------
- - AMF3 encoder reported incorrect byte length header for non-ASCII string
- data. Patch supplied by akaihola. (Ticket:194)
- - Decoder context not cleared between reading the remoting header and body.
- Reported by gerard (Ticket:192)
-
-0.1 (2008-02-11)
-----------------
- - New error handling api useful for registering custom exception classes
- (Ticket:185)
- - When a client receives a remoting error, an exception is generated
- (Ticket:167)
- - expose_request per service control vastly improved (Ticket:169)
- - Authentication per service control vastly improved (Ticket:166)
- - uuid is no longer installed when using Python 2.5 or newer (Ticket:182)
- - The inheritance tree was not consulted when encoding attributes (Ticket:172)
- - TypedObjects didn't work with old style classes (Ticket:171)
- - ErrorFault now prints details (Ticket:168)
- - Added expose_request to TwistedGateway (Ticket:165)
- - TwistedGateway now expects deferred from service functions (Ticket:164)
-
-0.1b (2008-01-13)
------------------
- - IExternalizable now takes its methods from the class and fine grain control
- over attr encoding (Ticket:110)
- - Added an adapter framework that gets imported when the related module is
- imported. See http://pyamf.org/wiki/AdapterFramework for more info
- - Added 'expose_environ' argument to WSGIGateway to expose the WSGI environ
- as the first arg in the called services.
- - Implemented Local Shared Object (LSO) support (Ticket:11)
- - ByteArray now implements IDataInput and IDataOutput instead of
- StringIOProxy (Ticket:151)
- - dicts are now used as the default for anonymous objects (Ticket:131)
- - remoting.client mostly fully supports the predefined headers (defined at
- http://osflash.org/documentation/amf/envelopes/remoting/headers). The only
- one missing is amf_server_debug (Ticket:39)
- - LazyImporter objects now set the __file__ attribute to None, so that
- querying sys.modules don't accidentally import the underlying module.
- (Ticket:147)
- - Fixed argument positioning for RemoteObject processing. Thanks akaihola!
- (Ticket:145)
- - ElementTree requirement is now ignored when using Python >= 2.5
- (Ticket:144)
- - Added tests for TwistedGateway (Ticket:132)
- - Workaround for Python 2.4 float shortcomings (Ticket:78)
- - Service Browser ('DescribeService' header) requests supported. (Ticket:138)
- - Remoting client now supports authentication. (Ticket:137)
- - Proper encoding for aliased subclassed builtin types, specifically
- flex.ArrayCollection.
- - Added support for easy encoding of Django object queries
- (Foo.objects.all())
- - Added 'add_type' allowing finer grain control of how a class is encoded in
- an AMF stream. (Ticket:130)
- - 'authenticator' keyword added to all gateways (moved from ServiceRequest)
- (Ticket:129)
- - Added 'expose_request' argument to DjangoGateway to expose the underlying
- HTTP Request object as the first arg in the called services. (Ticket:103)
-
-0.1.0a (2007-12-12)
--------------------
- - AMF0 and AMF3 encoders/decoders
- - Additional support for IExternalizable, ArrayCollection, ObjectProxy,
- ByteArray, RecordSet and RemoteObject
- - Remoting gateways for Twisted, WSGI, and Django
- - Authentication/setCredentials support
- - zlib compression support for ByteArray
- - Remoting client with httplib
-
-0.0.1 (2007-10-19)
-------------------
- - Started project based on previous work done in the RTMPy (http://rtmpy.org)
- project.
Copied: pyamf/branches/bytearray-context-695/CHANGES.txt (from rev 2928, \
pyamf/trunk/CHANGES.txt) \
===================================================================
--- pyamf/branches/bytearray-context-695/CHANGES.txt (rev 0)
+++ pyamf/branches/bytearray-context-695/CHANGES.txt 2009-10-19 10:33:37 UTC (rev \
2929) @@ -0,0 +1,347 @@
+Changelog
+=========
+
+This file contains information about the changes between the different
+versions of PyAMF.
+
+.. contents::
+
+0.6 (unreleased)
+----------------
+ - Fixed a reference bug with unsaved Django model instances. Thanks to wolver
+ for the patch and tests (Ticket:691).
+ - Fixed a small bug in decoding Django relations when declared as static.
+ Thanks to wolver for the patch. (Ticket:693)
+ - Removed ``exceptions`` from all ``Context`` and ``pyamf.util.Indexed*``
+ classes (Ticket:660)
+ - Empty Django relations are now encoded as ``None``, not ``pyamf.Undefined``
+ (Ticket:659)
+ - ``pyamf.ClientType`` removed, as it is not spec compliant (Ticket:651)
+
+0.5.1 (2009-09-19)
+------------------
+ - pyamf.register_package can now accept a list of classes (Ticket:650)
+ - Fixed a regression in TwistedGateway where services would be called twice
+ (Ticket:648)
+ - Fixed a bug with encoding anonymous trait references (Ticket:644)
+ - Moved IndexedCollection into cpyamf (Ticket:424)
+ - amf3.encode_int now encodes signed 29bit ints, not unsigned. Thanks to
+ gerard for the report, investigation and patch! (Ticket:646)
+
+0.5 (2009-09-07)
+----------------
+ - Added the ability to modify the timezones of dates being de/encoded for
+ legacy systems. (Ticket:612)
+ - Fixed a Django reference bug where parent.child.parent is parent would
+ encode 3 objects and not 2. (Ticket:642)
+ - Setting `None` on a Django DateField would blow up the encoder (Ticket:632)
+ - `rootCause` in ErrorFault objects are now populated with the traceback
+ object. (Ticket:637)
+ - Support for encoding/decoding BlazeDS specific messages (Ticket:581)
+ - Removed tracebacks from exception messages if debug=False (Ticket:552)
+ - Support for google.appengine.ext.db.polymodel.PolyModel (Ticket:633)
+ - Support for Django File/ImageField (Ticket:631)
+ - Support for Django model inheritance (Ticket:626)
+ - Rewrote attribute handling code. Added the ability to exclude, set
+ read-only and static attributes. See http://pyamf.org/wiki/AttributeControl
+ for more details. (Ticket:601)
+ - Exposed the amf request object if expose_request=True.
+ Twisted/Django/Google have an amf_request property on the http request
+ object. WSGI has an entry in the environ dict - pyamf.request (Ticket:234)
+ - Added support for the `array` module (Ticket:468)
+ - Fixed an issue with Django model AutoFields being set to 0 by the Flex
+ client (Ticket:556)
+ - Added support for the collections module. (Ticket:474)
+ - Added type checks for class objects. (Ticket:473)
+ - GAE FloatProperty can now accept int's without issue (Ticket:609)
+ - Revamped the lazy imports module to use sys.meta_path instead. Fixes all
+ erroneous import errors (Ticket:485).
+ - Django models will now accept dynamic properties (Ticket:575)
+ - Django PK properties will be set first to allow related properties to apply
+ correctly (Ticket:599)
+ - p.r.encode/p.r.decode both have a new logger kwarg. Supply a logging.Logger
+ instance if you want to see debug output (Ticket:588)
+ - pyamf.TypedObjectClassAlias now forces the class type (Ticket:537)
+ - If a call to Decoder.readElement results in an IOError exception, the
+ original position of the stream will be restored (Ticket:573)
+ - Replaced hardcoded Python implementation title in p.r.gateway.SERVER_NAME
+ (Ticket:541)
+ - Provide a user friendly way to handle datetime.time objects (Ticket:498)
+ - Removed the useless pyamf.logging module (Ticket:577)
+ - Added a new register_package helper function to make bulk class
+ registration easier. Check the docstring for more info. (Ticket:576)
+ - cpyamf.amf3._decode_int now propagates exceptions correctly (Ticket:506)
+ - Fixed an incorrect OverflowError when encoding large integers in AMF3
+ (Ticket:519)
+ - Added `append` to pyamf.util.BufferedByteStream (Ticket:574)
+ - Added Epydoc signatures to all util.StringIOProxy, util.DataTypeMixIn and
+ util.BufferedByteStream classes (Ticket:440)
+ - Removed dependancy on fpconst. Platforms with broken platforms are detected
+ and handled correctly internally (Ticket:564)
+ - amf0.Encoder now supports attribute ordering (Ticket:558)
+ - Removed duplicate method Context.reset, in favour of Context.clear
+ (Ticket:527)
+ - Rewrote cpyamf.util.BufferedByteStream to not depend on cStringIO. Added a
+ C based api for stream functions (Ticket:513)
+ - Unified exceptions for BufferedByteStream so that only IOError is raised.
+ (Ticket:520)
+ - Made raising pyamf.ReferenceError optional for all Indexed* and Context
+ classes. Refactored AMF3 so that only one ClassDefinition is created per
+ class (Ticket:524)
+ - Strict type checking now on pyamf.util.BufferedByteStream (Ticket:512)
+ - Removed the default loggers from p.r.gateway.*. To re-enable supply a
+ logger instance as a keyword arg to the gateway constructor (Ticket:525)
+ - Removed pyamf.util.make_classic_instance (as it is not used) (Ticket:521)
+ - Removed pyamf.util.get_mro, in favour of inspect.getmro (Ticket:526)
+ - Removed pyamf.util.Indexed[Collection|Map].remove (Ticket:518)
+
+0.4.2 (2009-04-20)
+------------------
+ - Support for decoding the 'source' property on ArrayCollections (Ticket:488)
+ - Fixed an issue in the GAE adapter where dynamic properties would be missing
+ on referenced objects. (Ticket:511)
+ - Fixed a critical issue with AMF0 reference counting when encoding remoting
+ responses. (Ticket:510)
+ - Strengthened http header handling in the client (Ticket:492)
+ - Support for Django i18n ugettext_lazy (Ticket:496)
+ - Added support for microseconds for datetime objects. Thanks to Derek Payton
+ for the patch. (Ticket:490)
+ - Added support for property types on SQLAlchemy mapped classes. (Ticket:491)
+ - Added support for property types for Google AppEngine db.Model and
+ db.Expando. (Ticket:487)
+
+0.4.1 (2009-02-23)
+------------------
+ - amf0.Encoder.use_amf3 has been extended to cover all object types
+ (Ticket:453,467)
+ - Property types on Django models will now encode as expected. (Ticket:480)
+ - Django models.ForeignKey properties will be followed only if previously
+ accessed outside of PyAMF. `_[attr]_cache` is no longer encoded.
+ (Ticket:456)
+ - Encoding {0:0, '0':1} will now raise an AttributeError. (Ticket:458)
+ - Google AppEngine adapter improvements - see ticket for details (Ticket:479)
+ - amf0.Encoder will encode all elements as AMF3 if use_amf3 option is set to
+ True (Ticket:453)
+ - Unicode handling in __repr__ functions has been improved (Ticket:455)
+ - object attributes and dict keys are now utf8 encoded bytestrings. Python
+ \**kwargs doesn't accept unicode key objects. (Ticket:463)
+ - Django models.TimeField, models.DateField will now be converted to the
+ correct type (datetime.time and datetime.date respectively).
+ fields.NOT_PROVIDED is also checked for by converting to pyamf.Undefined
+ and back again. (Ticket:457)
+
+0.4 (2009-01-18)
+----------------
+ - cpyamf now deals with exceptional floats the same way as pure python -
+ especially on windows (Ticket:448)
+ - Support for SQLAlchemy 0.5.1 (Ticket:449)
+ - amf0.Encoder now has a use_amf3 flag which determines which XML type to
+ return to the client (Ticket:435)
+ - BufferedByteStream.truncate(length) now actually does something useful
+ (Ticket:444)
+ - setup.py now gets the version number from pyamf/__init__.py source
+ (Ticket:429)
+
+0.4rc3 (2009-01-14)
+-------------------
+ - Support for SQLAlchemy 0.5.0 (Ticket:436)
+ - pyamf.util.DataTypeMixIn/cpyamf.util.BufferedByteStream can now
+ encode/decode 24bit un/signed integers. (Ticket:422)
+ - pyamf.util.StringIOProxy/cpyamf.util.BufferedByteStream both have new
+ consume methods that will chop of the tail of the stream (already read
+ stream). (Ticket:423)
+ - Now checking for all types of supported xml lib types for encoding, but
+ will only use the first implementation for decoding (Ticket:426)
+ - fpconst dependancy is now only required if the platform requires it
+ (Ticket:356)
+ - Decoding negative timestamps on certain platforms (namely Windows) are now
+ supported (Ticket:390)
+
+0.4rc2 (2009-01-05)
+-------------------
+ - Support for SQLAlchemy 0.4.0 (Ticket:410)
+
+0.4rc1 (2008-12-31)
+-------------------
+ - Support for encoding/decoding SQLAlchemy ORM objects with a new adapter.
+ Lots of people involved in this one, but special thanks to Dave Thompson
+ and Michael Van Tellingen for making this happen. (Ticket:277)
+ - All gateways now log exceptions when exceptions are raised during
+ en/decoding. (Ticket:394)
+ - Flex messaging now uses correct static attributes for encoding. Determining
+ static/dynamic attributes on objects is now easier (Ticket:357)
+ - Added use_proxy option to amf3 which will automagically convert ObjectProxy
+ to dict and ArrayCollection to list on decoding, and vice versa on encode.
+ Thanks to dthompso for the excellent patch (Ticket:355)
+ - flex.ArrayCollection now subclasses list instead of dict as non-int keys
+ are not allowed. IList interface has been implemented. (Ticket:349)
+ - Encoding registered subclass of list that define the 'externalised'
+ metadata will be encoded as an object.
+ - Encoders how have a 'strict' mode. Not generally useful for the time being
+ but will help with developments in the future. Type mapped functions now
+ require a second 'encoder' attribute. (Ticket:378)
+ - Added adapter to handle the decimal module and, if strict is set to False,
+ silently converts a Decimal instance to a float (Ticket:376)
+ - ClassAlias can now be subclassed and three new methods have been added:
+ applyAttributes, createInstance, getAttributes all which help to provide
+ fine control for object/instance manipulation (Ticket:348)
+ - Added support for __slots__ (Ticket:347)
+ - Fixed problem when decoding objects that map to GAE db.Model objects with
+ required properties (Ticket:342)
+ - ByteArray now does not throw an error when used in the Remoting framework
+ (Ticket:379)
+ - A new adapter that converts sets.ImmutableSet and sets.Set to tuples before
+ encoding. (Ticket:280)
+ - A revamped google app engine adapter that checks for the _key attribute in
+ an aliased class and first loads the object from the datastore and then
+ applies that properties in the object stream. (Ticket:307)
+ - New helper function to make it easier to manually add adapters (Ticket:350)
+ - Ability to disable the c extension with passing --disable-ext to setup.py
+ (Ticket:381, 391)
+ - Python C-extension for the pyamf.util.BufferedByteStream class. Originally
+ contributed by Gerard Escalante (Ticket:225, 405)
+ - New API to add headers such as cookies in pyamf.remoting.client
+ (Ticket:337)
+ - Now clearing the context between remoting requests (Ticket:309)
+ - Fixed issue with AMF3 class definition references (Ticket:341)
+ - More helpful description for register_class args check (Ticket:334)
+ - pyamf.register_class now checks to ensure that __init__ args do not have
+ any arguments (Ticket:322)
+ - Added RemoteObject support for AsyncMessage (Ticket:292)
+ - pyamf.remoting.ErrorFault.__repr__ now displays the traceback info (if it
+ exists). (Ticket:331)
+ - Both Encoders will now raise pyamf.EncodeError if a function is encoded
+ (Ticket:311)
+ - Twisted Gateway would fall over when returning tuples (Ticket:313)
+ - The remoting gateways now send a customizable Server header (Ticket:317)
+ - The remoting client now sends a customizable User-Agent header (Ticket:306)
+ - Added ability to set the HTTP referer in remoting client (Ticket:316)
+ - Fixed issue where the AMF3 encoder assumed objects with a 'tag' attribute
+ needed XML encoding. Reported by cy-man (Ticket:303)
+ - Solved issue with repr for AbstractMessage. Reported by datafunk
+ (Ticket:283)
+ - Content-type was missing in POST requests from the AMF client. Reported by
+ magog (Ticket:304)
+ - Added the disconnect Command operation (Ticket:325)
+ - Fixed issue with the unit tests for Django (Ticket:281)
+ - Removed the NetworkIOMixIn class (Ticket:232)
+
+0.3.1 (2008-05-04)
+------------------
+ - Importing module now has tests (Ticket:266)
+ - Django model adapter now imported only when django.db.models is imported
+ (Ticket:261)
+ - Google Model/Expando encoding now works out of the box
+ - Fixed issue with Remote Object destination (Ticket:270)
+ - Added a new gateway for the Google App Engine - see
+ pyamf.remoting.gateway.google.WebAppGateway (Ticket:253)
+ - amf0 Encoder now takes amf3 contexts into account (Ticket:268)
+ - amf*.encode helpers can now accept multiple arguments (Ticket:267)
+ - Removed the dependancy of fpconst for Python 2.5 or newer (Ticket:243)
+ - Solved issue with AMFPHP exceptions in AMF client (Ticket:258)
+ - Fixed issue with url parsing in AMF client (Ticket:256)
+ - Client no longer raises httplib.ResponseNotReady when making multiple
+ requests using the same RemotingService (Ticket:254)
+
+0.3 (2008-04-14)
+----------------
+ - Added compatibility module for Google App Engine (Ticket:247)
+ - Fixed the signed interpretation of compressed integers in AMF3 (Ticket:241)
+ - Classic class decoding would throw an AttributeError (Ticket:248)
+ - Reloading adapter modules caused errors in Django, Pylons and Google App
+ Engine. Resolved by removing dependancy on Importing module and
+ incorporating into pyamf.util (Ticket:250)
+ - Adapter framework can now be fired when only loading submodules
+ (Ticket:246)
+ - Made util.BufferedByteStream endian aware (Ticket:231)
+ - Fixed issue with Twisted threads (Ticket:233)
+
+0.2 (2008-03-12)
+----------------
+ - Removed amfinfo console_script (Ticket:226)
+ - Encoders/Decoders now check for __getstate__/__setstate__ respectively
+ (Ticket:209)
+ - Gateway import hack has now been removed - permanently (Ticket:224)
+ - Encoding/decoding performance has been increased 2x for AMF0 and up-to
+ 10x(!) for AMF3 (Ticket:198)
+ - Logging is now possible in all the supported gateways (Ticket:173)
+ - A new preprocessor function that runs after authentication, but before
+ invoking the service method (Ticket:196)
+ - authenticator can now be decorated with expose_request (Ticket:195)
+ - Python 2.3 support (Ticket:33)
+ - Python 2.6 support (Ticket:222)
+ - Made PyAMF distributable as zip-based Python Egg (Ticket:193)
+
+0.1.1 (2008-02-18)
+------------------
+ - AMF3 encoder reported incorrect byte length header for non-ASCII string
+ data. Patch supplied by akaihola. (Ticket:194)
+ - Decoder context not cleared between reading the remoting header and body.
+ Reported by gerard (Ticket:192)
+
+0.1 (2008-02-11)
+----------------
+ - New error handling api useful for registering custom exception classes
+ (Ticket:185)
+ - When a client receives a remoting error, an exception is generated
+ (Ticket:167)
+ - expose_request per service control vastly improved (Ticket:169)
+ - Authentication per service control vastly improved (Ticket:166)
+ - uuid is no longer installed when using Python 2.5 or newer (Ticket:182)
+ - The inheritance tree was not consulted when encoding attributes (Ticket:172)
+ - TypedObjects didn't work with old style classes (Ticket:171)
+ - ErrorFault now prints details (Ticket:168)
+ - Added expose_request to TwistedGateway (Ticket:165)
+ - TwistedGateway now expects deferred from service functions (Ticket:164)
+
+0.1b (2008-01-13)
+-----------------
+ - IExternalizable now takes its methods from the class and fine grain control
+ over attr encoding (Ticket:110)
+ - Added an adapter framework that gets imported when the related module is
+ imported. See http://pyamf.org/wiki/AdapterFramework for more info
+ - Added 'expose_environ' argument to WSGIGateway to expose the WSGI environ
+ as the first arg in the called services.
+ - Implemented Local Shared Object (LSO) support (Ticket:11)
+ - ByteArray now implements IDataInput and IDataOutput instead of
+ StringIOProxy (Ticket:151)
+ - dicts are now used as the default for anonymous objects (Ticket:131)
+ - remoting.client mostly fully supports the predefined headers (defined at
+ http://osflash.org/documentation/amf/envelopes/remoting/headers). The only
+ one missing is amf_server_debug (Ticket:39)
+ - LazyImporter objects now set the __file__ attribute to None, so that
+ querying sys.modules don't accidentally import the underlying module.
+ (Ticket:147)
+ - Fixed argument positioning for RemoteObject processing. Thanks akaihola!
+ (Ticket:145)
+ - ElementTree requirement is now ignored when using Python >= 2.5
+ (Ticket:144)
+ - Added tests for TwistedGateway (Ticket:132)
+ - Workaround for Python 2.4 float shortcomings (Ticket:78)
+ - Service Browser ('DescribeService' header) requests supported. (Ticket:138)
+ - Remoting client now supports authentication. (Ticket:137)
+ - Proper encoding for aliased subclassed builtin types, specifically
+ flex.ArrayCollection.
+ - Added support for easy encoding of Django object queries
+ (Foo.objects.all())
+ - Added 'add_type' allowing finer grain control of how a class is encoded in
+ an AMF stream. (Ticket:130)
+ - 'authenticator' keyword added to all gateways (moved from ServiceRequest)
+ (Ticket:129)
+ - Added 'expose_request' argument to DjangoGateway to expose the underlying
+ HTTP Request object as the first arg in the called services. (Ticket:103)
+
+0.1.0a (2007-12-12)
+-------------------
+ - AMF0 and AMF3 encoders/decoders
+ - Additional support for IExternalizable, ArrayCollection, ObjectProxy,
+ ByteArray, RecordSet and RemoteObject
+ - Remoting gateways for Twisted, WSGI, and Django
+ - Authentication/setCredentials support
+ - zlib compression support for ByteArray
+ - Remoting client with httplib
+
+0.0.1 (2007-10-19)
+------------------
+ - Started project based on previous work done in the RTMPy (http://rtmpy.org)
+ project.
Deleted: pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py
===================================================================
--- pyamf/trunk/pyamf/adapters/_django_db_models_base.py 2009-10-10 21:51:34 UTC (rev \
2927)
+++ pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py 2009-10-19 \
10:33:37 UTC (rev 2929) @@ -1,283 +0,0 @@
-# Copyright (c) 2007-2009 The PyAMF Project.
-# See LICENSE.txt for details.
-
-"""
-C{django.db.models} adapter module.
-
-@see: U{Django Project<http://www.djangoproject.com>}
-
-@since: 0.4.1
-"""
-
-from django.db.models.base import Model
-from django.db.models import fields
-from django.db.models.fields import related, files
-
-import datetime
-
-import pyamf
-from pyamf.util import imports
-
-
-class DjangoReferenceCollection(dict):
- """
- This helper class holds a dict of klass to pk/objects loaded from the
- underlying db.
-
- @since: 0.5
- """
-
- def _getClass(self, klass):
- if klass not in self.keys():
- self[klass] = {}
-
- return self[klass]
-
- def getClassKey(self, klass, key):
- """
- Return an instance based on klass/key.
-
- If an instance cannot be found then L{KeyError} is raised.
-
- @param klass: The class of the instance.
- @param key: The primary_key of the instance.
- @return: The instance linked to the C{klass}/C{key}.
- @rtype: Instance of L{klass}.
- """
- d = self._getClass(klass)
-
- return d[key]
-
- def addClassKey(self, klass, key, obj):
- """
- Adds an object to the collection, based on klass and key.
-
- @param klass: The class of the object.
- @param key: The datastore key of the object.
- @param obj: The loaded instance from the datastore.
- """
- d = self._getClass(klass)
-
- d[key] = obj
-
-
-class DjangoClassAlias(pyamf.ClassAlias):
- """
- """
-
- def getCustomProperties(self):
- self.fields = {}
- self.relations = {}
- self.columns = []
-
- self.meta = self.klass._meta
-
- for x in self.meta.local_fields:
- if isinstance(x, files.FileField):
- self.readonly_attrs.update([x.name])
-
- if not isinstance(x, related.ForeignKey):
- self.fields[x.name] = x
- else:
- self.relations[x.name] = x
-
- self.columns.append(x.attname)
-
- for k, v in self.klass.__dict__.iteritems():
- if isinstance(v, related.ReverseManyRelatedObjectsDescriptor):
- self.fields[k] = v.field
-
- parent_fields = []
-
- for field in self.meta.parents.values():
- parent_fields.append(field.attname)
- del self.relations[field.name]
-
- self.exclude_attrs.update(parent_fields)
-
- props = self.fields.keys()
-
- self.static_attrs.update(props)
- self.encodable_properties.update(props)
- self.decodable_properties.update(props)
-
- def _compile_base_class(self, klass):
- if klass is Model:
- return
-
- pyamf.ClassAlias._compile_base_class(self, klass)
-
- def _encodeValue(self, field, value):
- if value is fields.NOT_PROVIDED:
- return pyamf.Undefined
-
- if value is None:
- return value
-
- # deal with dates ..
- if isinstance(field, fields.DateTimeField):
- return value
- elif isinstance(field, fields.DateField):
- return datetime.datetime(value.year, value.month, value.day, 0, 0, 0)
- elif isinstance(field, fields.TimeField):
- return datetime.datetime(1970, 1, 1,
- value.hour, value.minute, value.second, value.microsecond)
- elif isinstance(value, files.FieldFile):
- return value.name
-
- return value
-
- def _decodeValue(self, field, value):
- if value is pyamf.Undefined:
- return fields.NOT_PROVIDED
-
- if isinstance(field, fields.AutoField) and value == 0:
- return None
- elif isinstance(field, fields.DateTimeField):
- # deal with dates
- return value
- elif isinstance(field, fields.DateField):
- if not value:
- return None
-
- return datetime.date(value.year, value.month, value.day)
- elif isinstance(field, fields.TimeField):
- if not value:
- return None
-
- return datetime.time(value.hour, value.minute, value.second, \
value.microsecond)
-
- return value
-
- def getEncodableAttributes(self, obj, **kwargs):
- sa, da = pyamf.ClassAlias.getEncodableAttributes(self, obj, **kwargs)
-
- for name, prop in self.fields.iteritems():
- if name not in sa:
- continue
-
- if isinstance(prop, related.ManyToManyField):
- sa[name] = [x for x in getattr(obj, name).all()]
- else:
- sa[name] = self._encodeValue(prop, getattr(obj, name))
-
- if not da:
- da = {}
-
- keys = da.keys()
-
- for key in keys:
- if key.startswith('_'):
- del da[key]
- elif key in self.columns:
- del da[key]
-
- for name, relation in self.relations.iteritems():
- if '_%s_cache' % name in obj.__dict__:
- da[name] = getattr(obj, name)
-
- if not da:
- da = None
-
- return sa, da
-
- def getDecodableAttributes(self, obj, attrs, **kwargs):
- attrs = pyamf.ClassAlias.getDecodableAttributes(self, obj, attrs, **kwargs)
-
- for n in self.decodable_properties:
- if n in self.relations:
- continue
-
- f = self.fields[n]
-
- attrs[f.attname] = self._decodeValue(f, attrs[n])
-
- # primary key of django object must always be set first for
- # relationships with other model objects to work properly
- # and dict.iteritems() does not guarantee order
- #
- # django also forces the use only one attribute as primary key, so
- # our obj._meta.pk.attname check is sufficient)
- try:
- setattr(obj, obj._meta.pk.attname, attrs[obj._meta.pk.attname])
- del attrs[obj._meta.pk.attname]
- except KeyError:
- pass
-
- return attrs
-
-
-def getDjangoObjects(context):
- """
- Returns a reference to the C{django_objects} on the context. If it doesn't
- exist then it is created.
-
- @param context: The context to load the C{django_objects} index from.
- @type context: Instance of L{pyamf.BaseContext}
- @return: The C{django_objects} index reference.
- @rtype: Instance of L{DjangoReferenceCollection}
- @since: 0.5
- """
- if not hasattr(context, 'django_objects'):
- context.django_objects = DjangoReferenceCollection()
-
- return context.django_objects
-
-
-def writeDjangoObject(self, obj, *args, **kwargs):
- """
- The Django ORM creates new instances of objects for each db request.
- This is a problem for PyAMF as it uses the id(obj) of the object to do
- reference checking.
-
- We could just ignore the problem, but the objects are conceptually the
- same so the effort should be made to attempt to resolve references for a
- given object graph.
-
- We create a new map on the encoder context object which contains a dict of
- C{object.__class__: {key1: object1, key2: object2, .., keyn: objectn}}. We
- use the primary key to do the reference checking.
-
- @since: 0.5
- """
- if not isinstance(obj, Model):
- self.writeNonDjangoObject(obj, *args, **kwargs)
-
- return
-
- context = self.context
- kls = obj.__class__
-
- s = obj.pk
-
- django_objects = getDjangoObjects(context)
-
- try:
- referenced_object = django_objects.getClassKey(kls, s)
- except KeyError:
- referenced_object = obj
- django_objects.addClassKey(kls, s, obj)
-
- self.writeNonDjangoObject(referenced_object, *args, **kwargs)
-
-
-def install_django_reference_model_hook(mod):
- """
- Called when L{pyamf.amf0} or L{pyamf.amf3} are imported. Attaches the
- L{writeDjangoObject} method to the C{Encoder} class in that module.
-
- @param mod: The module imported.
- @since: 0.4.1
- """
- if not hasattr(mod.Encoder, 'writeNonDjangoObject'):
- mod.Encoder.writeNonDjangoObject = mod.Encoder.writeObject
- mod.Encoder.writeObject = writeDjangoObject
-
-
-# initialise the module here: hook into pyamf
-
-pyamf.register_alias_type(DjangoClassAlias, Model)
-
-# hook the L{writeDjangobject} method to the Encoder class on import
-imports.when_imported('pyamf.amf0', install_django_reference_model_hook)
-imports.when_imported('pyamf.amf3', install_django_reference_model_hook)
Copied: pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py \
(from rev 2928, pyamf/trunk/pyamf/adapters/_django_db_models_base.py) \
===================================================================
--- pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py \
(rev 0)
+++ pyamf/branches/bytearray-context-695/pyamf/adapters/_django_db_models_base.py 2009-10-19 \
10:33:37 UTC (rev 2929) @@ -0,0 +1,288 @@
+# Copyright (c) 2007-2009 The PyAMF Project.
+# See LICENSE.txt for details.
+
+"""
+C{django.db.models} adapter module.
+
+@see: U{Django Project<http://www.djangoproject.com>}
+
+@since: 0.4.1
+"""
+
+from django.db.models.base import Model
+from django.db.models import fields
+from django.db.models.fields import related, files
+
+import datetime
+
+import pyamf
+from pyamf.util import imports
+
+
+class DjangoReferenceCollection(dict):
+ """
+ This helper class holds a dict of klass to pk/objects loaded from the
+ underlying db.
+
+ @since: 0.5
+ """
+
+ def _getClass(self, klass):
+ if klass not in self.keys():
+ self[klass] = {}
+
+ return self[klass]
+
+ def getClassKey(self, klass, key):
+ """
+ Return an instance based on klass/key.
+
+ If an instance cannot be found then L{KeyError} is raised.
+
+ @param klass: The class of the instance.
+ @param key: The primary_key of the instance.
+ @return: The instance linked to the C{klass}/C{key}.
+ @rtype: Instance of L{klass}.
+ """
+ d = self._getClass(klass)
+
+ return d[key]
+
+ def addClassKey(self, klass, key, obj):
+ """
+ Adds an object to the collection, based on klass and key.
+
+ @param klass: The class of the object.
+ @param key: The datastore key of the object.
+ @param obj: The loaded instance from the datastore.
+ """
+ d = self._getClass(klass)
+
+ d[key] = obj
+
+
+class DjangoClassAlias(pyamf.ClassAlias):
+ """
+ """
+
+ def getCustomProperties(self):
+ self.fields = {}
+ self.relations = {}
+ self.columns = []
+
+ self.meta = self.klass._meta
+
+ for x in self.meta.local_fields:
+ if isinstance(x, files.FileField):
+ self.readonly_attrs.update([x.name])
+
+ if not isinstance(x, related.ForeignKey):
+ self.fields[x.name] = x
+ else:
+ self.relations[x.name] = x
+
+ self.columns.append(x.attname)
+
+ for k, v in self.klass.__dict__.iteritems():
+ if isinstance(v, related.ReverseManyRelatedObjectsDescriptor):
+ self.fields[k] = v.field
+
+ parent_fields = []
+
+ for field in self.meta.parents.values():
+ parent_fields.append(field.attname)
+ del self.relations[field.name]
+
+ self.exclude_attrs.update(parent_fields)
+
+ props = self.fields.keys()
+
+ self.static_attrs.update(props)
+ self.encodable_properties.update(props)
+ self.decodable_properties.update(props)
+
+ def _compile_base_class(self, klass):
+ if klass is Model:
+ return
+
+ pyamf.ClassAlias._compile_base_class(self, klass)
+
+ def _encodeValue(self, field, value):
+ if value is fields.NOT_PROVIDED:
+ return pyamf.Undefined
+
+ if value is None:
+ return value
+
+ # deal with dates ..
+ if isinstance(field, fields.DateTimeField):
+ return value
+ elif isinstance(field, fields.DateField):
+ return datetime.datetime(value.year, value.month, value.day, 0, 0, 0)
+ elif isinstance(field, fields.TimeField):
+ return datetime.datetime(1970, 1, 1,
+ value.hour, value.minute, value.second, value.microsecond)
+ elif isinstance(value, files.FieldFile):
+ return value.name
+
+ return value
+
+ def _decodeValue(self, field, value):
+ if value is pyamf.Undefined:
+ return fields.NOT_PROVIDED
+
+ if isinstance(field, fields.AutoField) and value == 0:
+ return None
+ elif isinstance(field, fields.DateTimeField):
+ # deal with dates
+ return value
+ elif isinstance(field, fields.DateField):
+ if not value:
+ return None
+
+ return datetime.date(value.year, value.month, value.day)
+ elif isinstance(field, fields.TimeField):
+ if not value:
+ return None
+
+ return datetime.time(value.hour, value.minute, value.second, \
value.microsecond) +
+ return value
+
+ def getEncodableAttributes(self, obj, **kwargs):
+ sa, da = pyamf.ClassAlias.getEncodableAttributes(self, obj, **kwargs)
+
+ for name, prop in self.fields.iteritems():
+ if name not in sa:
+ continue
+
+ if isinstance(prop, related.ManyToManyField):
+ sa[name] = [x for x in getattr(obj, name).all()]
+ else:
+ sa[name] = self._encodeValue(prop, getattr(obj, name))
+
+ if not da:
+ da = {}
+
+ keys = da.keys()
+
+ for key in keys:
+ if key.startswith('_'):
+ del da[key]
+ elif key in self.columns:
+ del da[key]
+
+ for name, relation in self.relations.iteritems():
+ if '_%s_cache' % name in obj.__dict__:
+ da[name] = getattr(obj, name)
+
+ if not da:
+ da = None
+
+ return sa, da
+
+ def getDecodableAttributes(self, obj, attrs, **kwargs):
+ attrs = pyamf.ClassAlias.getDecodableAttributes(self, obj, attrs, **kwargs)
+
+ for n in self.decodable_properties:
+ if n in self.relations:
+ continue
+
+ f = self.fields[n]
+
+ attrs[f.attname] = self._decodeValue(f, attrs[n])
+
+ # primary key of django object must always be set first for
+ # relationships with other model objects to work properly
+ # and dict.iteritems() does not guarantee order
+ #
+ # django also forces the use only one attribute as primary key, so
+ # our obj._meta.pk.attname check is sufficient)
+ try:
+ setattr(obj, obj._meta.pk.attname, attrs[obj._meta.pk.attname])
+ del attrs[obj._meta.pk.attname]
+ except KeyError:
+ pass
+
+ return attrs
+
+
+def getDjangoObjects(context):
+ """
+ Returns a reference to the C{django_objects} on the context. If it doesn't
+ exist then it is created.
+
+ @param context: The context to load the C{django_objects} index from.
+ @type context: Instance of L{pyamf.BaseContext}
+ @return: The C{django_objects} index reference.
+ @rtype: Instance of L{DjangoReferenceCollection}
+ @since: 0.5
+ """
+ if not hasattr(context, 'django_objects'):
+ context.django_objects = DjangoReferenceCollection()
+
+ return context.django_objects
+
+
+def writeDjangoObject(self, obj, *args, **kwargs):
+ """
+ The Django ORM creates new instances of objects for each db request.
+ This is a problem for PyAMF as it uses the id(obj) of the object to do
+ reference checking.
+
+ We could just ignore the problem, but the objects are conceptually the
+ same so the effort should be made to attempt to resolve references for a
+ given object graph.
+
+ We create a new map on the encoder context object which contains a dict of
+ C{object.__class__: {key1: object1, key2: object2, .., keyn: objectn}}. We
+ use the primary key to do the reference checking.
+
+ @since: 0.5
+ """
+ if not isinstance(obj, Model):
+ self.writeNonDjangoObject(obj, *args, **kwargs)
+
+ return
+
+ context = self.context
+ kls = obj.__class__
+
+ s = obj.pk
+
+ if s is None:
+ self.writeNonDjangoObject(obj, *args, **kwargs)
+
+ return
+
+ django_objects = getDjangoObjects(context)
+
+ try:
+ referenced_object = django_objects.getClassKey(kls, s)
+ except KeyError:
+ referenced_object = obj
+ django_objects.addClassKey(kls, s, obj)
+
+ self.writeNonDjangoObject(referenced_object, *args, **kwargs)
+
+
+def install_django_reference_model_hook(mod):
+ """
+ Called when L{pyamf.amf0} or L{pyamf.amf3} are imported. Attaches the
+ L{writeDjangoObject} method to the C{Encoder} class in that module.
+
+ @param mod: The module imported.
+ @since: 0.4.1
+ """
+ if not hasattr(mod.Encoder, 'writeNonDjangoObject'):
+ mod.Encoder.writeNonDjangoObject = mod.Encoder.writeObject
+ mod.Encoder.writeObject = writeDjangoObject
+
+
+# initialise the module here: hook into pyamf
+
+pyamf.register_alias_type(DjangoClassAlias, Model)
+
+# hook the L{writeDjangobject} method to the Encoder class on import
+imports.when_imported('pyamf.amf0', install_django_reference_model_hook)
+imports.when_imported('pyamf.amf3', install_django_reference_model_hook)
Deleted: pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py
===================================================================
--- pyamf/trunk/pyamf/tests/adapters/test_django.py 2009-10-10 21:51:34 UTC (rev \
2927)
+++ pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py 2009-10-19 \
10:33:37 UTC (rev 2929) @@ -1,829 +0,0 @@
-# Copyright (c) 2007-2009 The PyAMF Project.
-# See LICENSE.txt for details.
-
-"""
-PyAMF Django adapter tests.
-
-@since: 0.3.1
-"""
-
-import unittest
-import sys
-import os
-import new
-import datetime
-
-import pyamf
-from pyamf.tests import util
-
-
-class ModelsBaseTestCase(unittest.TestCase):
- def setUp(self):
- self.old_env = os.environ.copy()
- self.mods = sys.modules.copy()
-
- if 'DJANGO_SETTINGS_MODULE' in os.environ.keys():
- from django import conf
- import copy
-
- self.mod = copy.deepcopy(conf.settings)
- mod = conf.settings
- self.existing = True
- else:
- os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
- mod = new.module('settings')
-
- sys.modules['settings'] = mod
-
- self.existing = False
-
- app = new.module('adapters')
-
- app_models = new.module('adapters.models')
- setattr(app, 'models', app_models)
- setattr(app, '__file__', '')
- setattr(app_models, '__file__', '')
-
- sys.modules['adapters'] = app
- sys.modules['adapters.models'] = app_models
-
- self.app = app
- self.models = app_models
-
- setattr(mod, 'DATABASE_ENGINE', 'sqlite3')
- setattr(mod, 'DATABASE_NAME', ':memory:')
- setattr(mod, 'INSTALLED_APPS', ('adapters',))
- setattr(mod, 'USE_I18N', False)
-
- from pyamf.adapters import _django_db_models_base as models_adapter
-
- self.adapter = models_adapter
-
- def tearDown(self):
- util.replace_dict(os.environ, self.old_env)
- util.replace_dict(sys.modules, self.mods)
-
- if self.existing:
- from django import conf
- conf.settings = self.mod
-
- def resetDB(self):
- from django.db import connection
- import sys
-
- old_stderr = sys.stderr
- sys.stderr = util.NullFileDescriptor()
-
- self.db_name = connection.creation.create_test_db(0, autoclobber=True)
-
- sys.stderr = old_stderr
-
-
-class TypeMapTestCase(ModelsBaseTestCase):
- def test_objects_all(self):
- from django.db import models
-
- class Spam(models.Model):
- pass
-
- self.resetDB()
- encoder = pyamf.get_encoder(pyamf.AMF0)
-
- encoder.writeElement(Spam.objects.all())
- self.assertEquals(encoder.stream.getvalue(), '\n\x00\x00\x00\x00')
-
- encoder = pyamf.get_encoder(pyamf.AMF3)
- encoder.writeElement(Spam.objects.all())
- self.assertEquals(encoder.stream.getvalue(), '\t\x01\x01')
-
- def test_NOT_PROVIDED(self):
- from django.db.models import fields
-
- encoder = pyamf.get_encoder(pyamf.AMF0)
-
- encoder.writeElement(fields.NOT_PROVIDED)
- self.assertEquals(encoder.stream.getvalue(), '\x06')
-
- encoder = pyamf.get_encoder(pyamf.AMF3)
- encoder.writeElement(fields.NOT_PROVIDED)
- self.assertEquals(encoder.stream.getvalue(), '\x00')
-
-
-class ClassAliasTestCase(ModelsBaseTestCase):
- def test_time(self):
- from django.db import models
-
- class TestClass(models.Model):
- t = models.TimeField()
- d = models.DateField()
- dt = models.DateTimeField()
-
- x = TestClass()
-
- x.t = datetime.time(12, 12, 12)
- x.d = datetime.date(2008, 3, 12)
- x.dt = datetime.datetime(2008, 3, 12, 12, 12, 12)
-
- alias = self.adapter.DjangoClassAlias(TestClass, None)
-
- sa, da = alias.getEncodableAttributes(x)
-
- self.assertEquals(sa, {
- 'id': None,
- 'd': datetime.datetime(2008, 3, 12, 0, 0),
- 'dt': datetime.datetime(2008, 3, 12, 12, 12, 12),
- 't': datetime.datetime(1970, 1, 1, 12, 12, 12)
- })
-
- self.assertEquals(da, None)
-
- y = TestClass()
-
- alias.applyAttributes(y, {
- 'id': None,
- 'd': datetime.datetime(2008, 3, 12, 0, 0),
- 'dt': datetime.datetime(2008, 3, 12, 12, 12, 12),
- 't': datetime.datetime(1970, 1, 1, 12, 12, 12)
- })
-
- self.assertEquals(y.id, None)
- self.assertEquals(y.d, datetime.date(2008, 3, 12))
- self.assertEquals(y.dt, datetime.datetime(2008, 3, 12, 12, 12, 12))
- self.assertEquals(y.t, datetime.time(12, 12, 12))
-
- y = TestClass()
-
- alias.applyAttributes(y, {
- 'id': None,
- 'd': None,
- 'dt': None,
- 't': None
- })
-
- self.assertEquals(y.id, None)
- self.assertEquals(y.d, None)
- self.assertEquals(y.dt, None)
- self.assertEquals(y.t, None)
-
- def test_undefined(self):
- from django.db import models
- from django.db.models import fields
-
- class UndefinedClass(models.Model):
- pass
-
- alias = self.adapter.DjangoClassAlias(UndefinedClass, None)
-
- x = UndefinedClass()
-
- alias.applyAttributes(x, {
- 'id': pyamf.Undefined
- })
-
- self.assertEquals(x.id, fields.NOT_PROVIDED)
-
- x.id = fields.NOT_PROVIDED
-
- sa, da = alias.getEncodableAttributes(x)
-
- self.assertEquals(da, None)
- self.assertEquals(sa, {'id': pyamf.Undefined})
-
- def test_non_field_prop(self):
- from django.db import models
-
- class Book(models.Model):
- def _get_number_of_odd_pages(self):
- return 234
-
- # note the lack of a setter callable ..
- numberOfOddPages = property(_get_number_of_odd_pages)
-
- alias = self.adapter.DjangoClassAlias(Book, 'Book')
-
- x = Book()
-
- self.assertEquals(alias.getEncodableAttributes(x), (
- {'id': None},
- {'numberOfOddPages': 234}
- ))
-
- # now we test sending the numberOfOddPages attribute
- alias.applyAttributes(x, {'numberOfOddPages': 24, 'id': None})
-
- # test it hasn't been set
- self.assertEquals(x.numberOfOddPages, 234)
-
- def test_dynamic(self):
- """
- Test for dynamic property encoding.
- """
- from django.db import models
-
- class Foo(models.Model):
- pass
-
- alias = self.adapter.DjangoClassAlias(Foo, 'Book')
-
- x = Foo()
- x.spam = 'eggs'
-
- self.assertEquals(alias.getEncodableAttributes(x), (
- {'id': None},
- {'spam': 'eggs'}
- ))
-
- # now we test sending the numberOfOddPages attribute
- alias.applyAttributes(x, {'spam': 'foo', 'id': None})
-
- # test it has been set
- self.assertEquals(x.spam, 'foo')
-
-
-class ForeignKeyTestCase(ModelsBaseTestCase):
- def test_one_to_many(self):
- from django.db import models
-
- class Reporter(models.Model):
- first_name = models.CharField(max_length=30)
- last_name = models.CharField(max_length=30)
- email = models.EmailField()
-
- def __unicode__(self):
- return u"%s %s" % (self.first_name, self.last_name)
-
- class Article(models.Model):
- headline = models.CharField(max_length=100)
- pub_date = models.DateField()
- reporter = models.ForeignKey(Reporter)
-
- def __unicode__(self):
- return self.headline
-
- self.resetDB()
-
- # initialise the db ..
- r = Reporter(first_name='John', last_name='Smith', email='john@example.com')
- r.save()
-
- r2 = Reporter(first_name='Paul', last_name='Jones', \
email='paul@example.com')
- r2.save()
-
- a = Article(headline="This is a test", pub_date=datetime.date(2005, 7, 27), \
reporter=r)
- a.save()
-
- self.assertEquals(a.id, 1)
-
- del a
-
- a = Article.objects.filter(pk=1)[0]
-
- self.assertFalse('_reporter_cache' in a.__dict__)
- a.reporter
- self.assertTrue('_reporter_cache' in a.__dict__)
-
- del a
-
- a = Article.objects.filter(pk=1)[0]
- alias = self.adapter.DjangoClassAlias(Article, defer=True)
-
- self.assertFalse(hasattr(alias, 'fields'))
- sa, da = alias.getEncodableAttributes(a)
- self.assertEquals(sa, {
- 'headline': u'This is a test',
- 'pub_date': datetime.datetime(2005, 7, 27, 0, 0),
- 'id': 1,
- })
- # note that the reporter attribute does not exist.
- self.assertEquals(da, None)
-
- self.assertFalse('_reporter_cache' in a.__dict__)
- self.assertEquals(pyamf.encode(a, encoding=pyamf.AMF3).getvalue(),
- '\n;\x01\x11headline\x05id\x11pub_date\x06\x1dThis is a test\x04'
- '\x01\x08\x01BpUYj@\x00\x00\x01')
-
- del a
-
- # now with select_related to pull in the reporter object
- a = Article.objects.select_related().filter(pk=1)[0]
-
- alias = self.adapter.DjangoClassAlias(Article, defer=True)
-
- self.assertFalse(hasattr(alias, 'fields'))
- self.assertEquals(alias.getEncodableAttributes(a), ({
- 'headline': u'This is a test',
- 'pub_date': datetime.datetime(2005, 7, 27, 0, 0),
- 'id': 1,
- },
- {
- 'reporter': r,
- }))
-
- self.assertTrue('_reporter_cache' in a.__dict__)
- self.assertEquals(pyamf.encode(a, encoding=pyamf.AMF3).getvalue(),
- '\n;\x01\x11headline\x05id\x11pub_date\x06\x1dThis is a test\x04'
- '\x01\x08\x01BpUYj@\x00\x00\x11reporter\nK\x01\x0bemail\x15'
- 'first_name\x02\x13last_name\x06!john@example.com\x06\tJohn\x04'
- '\x01\x06\x0bSmith\x01\x01')
-
- def test_many_to_many(self):
- from django.db import models
-
- class Publication(models.Model):
- title = models.CharField(max_length=30)
-
- def __unicode__(self):
- return self.title
-
- class Meta:
- ordering = ('title',)
-
- class Article2(models.Model):
- headline = models.CharField(max_length=100)
- publications = models.ManyToManyField(Publication)
-
- def __unicode__(self):
- return self.headline
-
- class Meta:
- ordering = ('headline',)
-
- self.resetDB()
-
- # install some test data - taken from
- # http://www.djangoproject.com/documentation/models/many_to_many/
- p1 = Publication(id=None, title='The Python Journal')
- p1.save()
- p2 = Publication(id=None, title='Science News')
- p2.save()
- p3 = Publication(id=None, title='Science Weekly')
- p3.save()
-
- # Create an Article.
- a1 = Article2(id=None, headline='Django lets you build Web apps easily')
- a1.save()
- self.assertEquals(a1.id, 1)
-
- # Associate the Article with a Publication.
- a1.publications.add(p1)
-
- pub_alias = self.adapter.DjangoClassAlias(Publication, None)
- art_alias = self.adapter.DjangoClassAlias(Article2, None)
-
- test_publication = Publication.objects.filter(pk=1)[0]
- test_article = Article2.objects.filter(pk=1)[0]
-
- sa, da = pub_alias.getEncodableAttributes(test_publication)
- self.assertEquals(sa, {'id': 1, 'title': u'The Python Journal'})
- self.assertEquals(da, None)
-
- sa, da = art_alias.getEncodableAttributes(test_article)
- self.assertEquals(sa, {
- 'headline': u'Django lets you build Web apps easily',
- 'id': 1,
- 'publications': [p1]
- })
- self.assertEquals(da, None)
-
- x = Article2()
- art_alias.applyAttributes(x, {
- 'headline': u'Test',
- 'id': 1,
- 'publications': [p1]
- })
-
- self.assertEquals(x.headline, u'Test')
- self.assertEquals(x.id, 1)
-
- p = x.publications.all()
-
- self.assertEquals(len(p), 1)
- self.assertEquals(p[0], p1)
-
- def test_nullable_foreign_keys(self):
- from django.db import models
-
- class FooBar(models.Model):
- pass
-
- class NullForeignKey(models.Model):
- foobar = models.ForeignKey(FooBar, null=True)
-
- class BlankForeignKey(models.Model):
- foobar = models.ForeignKey(FooBar, blank=True)
-
- self.resetDB()
-
- x = FooBar()
- x.save()
-
- nfk_alias = self.adapter.DjangoClassAlias(NullForeignKey, None)
- bfk_alias = self.adapter.DjangoClassAlias(BlankForeignKey, None)
-
- nfk = NullForeignKey()
-
- sa, da = nfk_alias.getEncodableAttributes(nfk)
-
- self.assertEquals(sa, {'id': None})
- self.assertEquals(da, None)
-
- def test_static_relation(self):
- """
- @see: #693
- """
- from django.db import models
- from pyamf import util
-
- class Gak(models.Model):
- pass
-
- class Baz(models.Model):
- gak = models.ForeignKey(Gak)
-
- class __amf__:
- static = ('gak',)
-
- self.resetDB()
-
- alias = self.adapter.DjangoClassAlias(Baz, **util.get_class_meta(Baz))
-
- alias.compile()
-
- self.assertTrue('gak' in alias.relations)
- self.assertTrue('gak' in alias.decodable_properties)
- self.assertTrue('gak' in alias.static_attrs)
-
- x = Baz()
-
- alias.getDecodableAttributes(x, {'id': None, 'gak': 'foo'})
-
-
-class I18NTestCase(ModelsBaseTestCase):
- def test_encode(self):
- from django.utils.translation import ugettext_lazy
-
- self.assertEquals(pyamf.encode(ugettext_lazy('Hello')).getvalue(),
- '\x02\x00\x05Hello')
-
-
-class PKTestCase(ModelsBaseTestCase):
- """
- See ticket #599 for this. Check to make sure that django pk fields
- are set first
- """
-
- def test_behaviour(self):
- from django.db import models
-
- class Publication(models.Model):
- title = models.CharField(max_length=30)
-
- def __unicode__(self):
- return self.title
-
- class Meta:
- ordering = ('title',)
-
- class Article2(models.Model):
- headline = models.CharField(max_length=100)
- publications = models.ManyToManyField(Publication)
-
- def __unicode__(self):
- return self.headline
-
- class Meta:
- ordering = ('headline',)
-
- self.resetDB()
-
- p = Publication(id=None, title='The Python Journal')
- a = Article2(id=None, headline='Django lets you build Web apps easily')
-
- # Associate the Article with a Publication.
- self.assertRaises(ValueError, lambda a, p: a.publications.add(p), a, p)
-
- p.save()
- a.save()
-
- self.assertEquals(a.id, 2)
-
- article_alias = self.adapter.DjangoClassAlias(Article2, None)
- x = Article2()
-
- article_alias.applyAttributes(x, {
- 'headline': 'Foo bar!',
- 'id': 2,
- 'publications': [p]
- })
-
- def test_none(self):
- """
- See #556. Make sure that PK fields with a value of 0 are actually set
- to C{None}.
- """
- from django.db import models
-
- class Foo(models.Model):
- pass
-
- self.resetDB()
-
- alias = self.adapter.DjangoClassAlias(Foo, None)
-
- x = Foo()
-
- self.assertEquals(x.id, None)
-
- alias.applyAttributes(x, {
- 'id': 0
- })
-
- self.assertEquals(x.id, None)
-
-
-class ModelInheritanceTestCase(ModelsBaseTestCase):
- """
- Tests for L{Django model \
inheritance<http://docs.djangoproject.com/en/dev/topics/db/models/#model-inheritance>}
- """
-
- def test_abstract(self):
- from django.db import models
-
- class CommonInfo(models.Model):
- name = models.CharField(max_length=100)
- age = models.PositiveIntegerField()
-
- class Meta:
- abstract = True
-
- class Student(CommonInfo):
- home_group = models.CharField(max_length=5)
-
- self.resetDB()
-
- alias = self.adapter.DjangoClassAlias(Student)
-
- x = Student()
-
- sa, da = alias.getEncodableAttributes(x)
-
- self.assertEquals(sa, {
- 'age': None,
- 'home_group': '',
- 'id': None,
- 'name': ''
- })
-
- self.assertEquals(da, None)
-
- def test_concrete(self):
- from django.db import models
-
- class Place(models.Model):
- name = models.CharField(max_length=50)
- address = models.CharField(max_length=80)
-
- class Restaurant(Place):
- serves_hot_dogs = models.BooleanField()
- serves_pizza = models.BooleanField()
-
- self.resetDB()
-
- alias = self.adapter.DjangoClassAlias(Place)
- x = Place()
-
- sa, da = alias.getEncodableAttributes(x)
-
- self.assertEquals(sa, {
- 'id': None,
- 'name': '',
- 'address': ''
- })
-
- self.assertEquals(da, None)
-
- alias = self.adapter.DjangoClassAlias(Restaurant)
- x = Restaurant()
-
- sa, da = alias.getEncodableAttributes(x)
-
- self.assertEquals(sa, {
- 'id': None,
- 'name': '',
- 'address': '',
- 'serves_hot_dogs': False,
- 'serves_pizza': False
- })
-
- self.assertEquals(da, None)
-
-
-class MockFile(object):
- """
- mock for L{django.core.files.base.File}
- """
-
- def chunks(self):
- return []
-
- def __len__(self):
- return 0
-
- def read(self, n):
- return ''
-
-
-class FieldsTestCase(ModelsBaseTestCase):
- """
- Tests for L{fields}
- """
-
- def tearDown(self):
- ModelsBaseTestCase.tearDown(self)
-
- try:
- os.unlink(os.path.join(os.getcwd(), 'foo'))
- except OSError:
- raise
- pass
-
- def test_file(self):
- from django.db import models
-
- self.executed = False
-
- def get_studio_watermark(*args, **kwargs):
- self.executed = True
-
- return 'foo'
-
- class Image(models.Model):
- file = models.FileField(upload_to=get_studio_watermark)
- text = models.CharField(max_length=64)
-
- self.resetDB()
-
- alias = self.adapter.DjangoClassAlias(Image)
-
- i = Image()
- i.file.save('bar', MockFile())
-
- i.save()
-
- sa, da = alias.getEncodableAttributes(i)
-
- self.assertEquals(sa, {'text': '', 'id': 1, 'file': u'foo'})
- self.assertEquals(da, None)
- self.assertTrue(self.executed)
-
- attrs = alias.getDecodableAttributes(i, sa)
-
- self.assertEquals(attrs, {'text': ''})
-
-
-class ImageTestCase(ModelsBaseTestCase):
- """
- Tests for L{fields}
- """
-
- def test_image(self):
- from django.db import models
-
- self.executed = False
-
- def get_studio_watermark(*args, **kwargs):
- self.executed = True
-
- return 'foo'
-
- class Profile(models.Model):
- file = models.ImageField(upload_to=get_studio_watermark)
- text = models.CharField(max_length=64)
-
- self.resetDB()
-
- alias = self.adapter.DjangoClassAlias(Profile)
-
- i = Profile()
- i.file.save('bar', MockFile())
-
- i.save()
-
- sa, da = alias.getEncodableAttributes(i)
-
- self.assertEquals(sa, {'text': '', 'id': 1, 'file': u'foo_'})
- self.assertEquals(da, None)
- self.assertTrue(self.executed)
-
- attrs = alias.getDecodableAttributes(i, sa)
-
- self.assertEquals(attrs, {'text': ''})
-
-
-class ReferenceTestCase(ModelsBaseTestCase):
- """
- Test case to make sure that the same object from the database is encoded
- by reference.
- """
-
- def setUp(self):
- ModelsBaseTestCase.setUp(self)
-
- from django.db import models
-
- class ParentReference(models.Model):
- name = models.CharField(max_length=100)
- bar = models.ForeignKey('ChildReference', null=True)
-
- class ChildReference(models.Model):
- name = models.CharField(max_length=100)
- foo = models.ForeignKey(ParentReference)
-
- self.ParentReference = ParentReference
- self.ChildReference = ChildReference
-
- self.resetDB()
-
- def tearDown(self):
- ModelsBaseTestCase.tearDown(self)
-
- def test_not_referenced(self):
- """
- Test to ensure that we observe the correct behaviour in the Django
- ORM.
- """
- f = self.ParentReference()
- f.name = 'foo'
-
- b = self.ChildReference()
- b.name = 'bar'
-
- f.save()
- b.foo = f
- b.save()
- f.bar = b
- f.save()
-
- self.assertEquals(f.id, 1)
- foo = self.ParentReference.objects.select_related().get(id=1)
-
- self.assertFalse(foo.bar.foo is foo)
-
- def test_referenced_encode(self):
- f = self.ParentReference()
- f.name = 'foo'
-
- b = self.ChildReference()
- b.name = 'bar'
-
- f.save()
- b.foo = f
- b.save()
- f.bar = b
- f.save()
-
- self.assertEquals(f.id, 2)
- foo = self.ParentReference.objects.select_related().get(id=2)
-
- # ensure the referenced attribute resolves
- foo.bar.foo
-
- self.assertEquals(pyamf.encode(foo).getvalue(), '\x03\x00\x02id\x00'
- '@\x00\x00\x00\x00\x00\x00\x00\x00\x04name\x02\x00\x03foo\x00'
- '\x03bar\x03\x00\x02id\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x04na'
- 'me\x02\x00\x03bar\x00\x03foo\x07\x00\x00\x00\x00\t\x00\x00\t')
-
-
-def suite():
- suite = unittest.TestSuite()
-
- try:
- import django
- except ImportError:
- return suite
-
- test_cases = [
- TypeMapTestCase,
- ClassAliasTestCase,
- ForeignKeyTestCase,
- I18NTestCase,
- PKTestCase,
- ModelInheritanceTestCase,
- FieldsTestCase,
- ReferenceTestCase
- ]
-
- try:
- import PIL
- except:
- pass
- else:
- test_cases.append(ImageTestCase)
-
- for tc in test_cases:
- suite.addTest(unittest.makeSuite(tc))
-
- return suite
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
Copied: pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py \
(from rev 2928, pyamf/trunk/pyamf/tests/adapters/test_django.py) \
===================================================================
--- pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py \
(rev 0)
+++ pyamf/branches/bytearray-context-695/pyamf/tests/adapters/test_django.py 2009-10-19 \
10:33:37 UTC (rev 2929) @@ -0,0 +1,845 @@
+# Copyright (c) 2007-2009 The PyAMF Project.
+# See LICENSE.txt for details.
+
+"""
+PyAMF Django adapter tests.
+
+@since: 0.3.1
+"""
+
+import unittest
+import sys
+import os
+import new
+import datetime
+
+import pyamf
+from pyamf.tests import util
+
+
+class ModelsBaseTestCase(unittest.TestCase):
+ def setUp(self):
+ self.old_env = os.environ.copy()
+ self.mods = sys.modules.copy()
+
+ if 'DJANGO_SETTINGS_MODULE' in os.environ.keys():
+ from django import conf
+ import copy
+
+ self.mod = copy.deepcopy(conf.settings)
+ mod = conf.settings
+ self.existing = True
+ else:
+ os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+ mod = new.module('settings')
+
+ sys.modules['settings'] = mod
+
+ self.existing = False
+
+ app = new.module('adapters')
+
+ app_models = new.module('adapters.models')
+ setattr(app, 'models', app_models)
+ setattr(app, '__file__', '')
+ setattr(app_models, '__file__', '')
+
+ sys.modules['adapters'] = app
+ sys.modules['adapters.models'] = app_models
+
+ self.app = app
+ self.models = app_models
+
+ setattr(mod, 'DATABASE_ENGINE', 'sqlite3')
+ setattr(mod, 'DATABASE_NAME', ':memory:')
+ setattr(mod, 'INSTALLED_APPS', ('adapters',))
+ setattr(mod, 'USE_I18N', False)
+
+ from pyamf.adapters import _django_db_models_base as models_adapter
+
+ self.adapter = models_adapter
+
+ def tearDown(self):
+ util.replace_dict(os.environ, self.old_env)
+ util.replace_dict(sys.modules, self.mods)
+
+ if self.existing:
+ from django import conf
+ conf.settings = self.mod
+
+ def resetDB(self):
+ from django.db import connection
+ import sys
+
+ old_stderr = sys.stderr
+ sys.stderr = util.NullFileDescriptor()
+
+ self.db_name = connection.creation.create_test_db(0, autoclobber=True)
+
+ sys.stderr = old_stderr
+
+
+class TypeMapTestCase(ModelsBaseTestCase):
+ def test_objects_all(self):
+ from django.db import models
+
+ class Spam(models.Model):
+ pass
+
+ self.resetDB()
+ encoder = pyamf.get_encoder(pyamf.AMF0)
+
+ encoder.writeElement(Spam.objects.all())
+ self.assertEquals(encoder.stream.getvalue(), '\n\x00\x00\x00\x00')
+
+ encoder = pyamf.get_encoder(pyamf.AMF3)
+ encoder.writeElement(Spam.objects.all())
+ self.assertEquals(encoder.stream.getvalue(), '\t\x01\x01')
+
+ def test_NOT_PROVIDED(self):
+ from django.db.models import fields
+
+ encoder = pyamf.get_encoder(pyamf.AMF0)
+
+ encoder.writeElement(fields.NOT_PROVIDED)
+ self.assertEquals(encoder.stream.getvalue(), '\x06')
+
+ encoder = pyamf.get_encoder(pyamf.AMF3)
+ encoder.writeElement(fields.NOT_PROVIDED)
+ self.assertEquals(encoder.stream.getvalue(), '\x00')
+
+
+class ClassAliasTestCase(ModelsBaseTestCase):
+ def test_time(self):
+ from django.db import models
+
+ class TestClass(models.Model):
+ t = models.TimeField()
+ d = models.DateField()
+ dt = models.DateTimeField()
+
+ x = TestClass()
+
+ x.t = datetime.time(12, 12, 12)
+ x.d = datetime.date(2008, 3, 12)
+ x.dt = datetime.datetime(2008, 3, 12, 12, 12, 12)
+
+ alias = self.adapter.DjangoClassAlias(TestClass, None)
+
+ sa, da = alias.getEncodableAttributes(x)
+
+ self.assertEquals(sa, {
+ 'id': None,
+ 'd': datetime.datetime(2008, 3, 12, 0, 0),
+ 'dt': datetime.datetime(2008, 3, 12, 12, 12, 12),
+ 't': datetime.datetime(1970, 1, 1, 12, 12, 12)
+ })
+
+ self.assertEquals(da, None)
+
+ y = TestClass()
+
+ alias.applyAttributes(y, {
+ 'id': None,
+ 'd': datetime.datetime(2008, 3, 12, 0, 0),
+ 'dt': datetime.datetime(2008, 3, 12, 12, 12, 12),
+ 't': datetime.datetime(1970, 1, 1, 12, 12, 12)
+ })
+
+ self.assertEquals(y.id, None)
+ self.assertEquals(y.d, datetime.date(2008, 3, 12))
+ self.assertEquals(y.dt, datetime.datetime(2008, 3, 12, 12, 12, 12))
+ self.assertEquals(y.t, datetime.time(12, 12, 12))
+
+ y = TestClass()
+
+ alias.applyAttributes(y, {
+ 'id': None,
+ 'd': None,
+ 'dt': None,
+ 't': None
+ })
+
+ self.assertEquals(y.id, None)
+ self.assertEquals(y.d, None)
+ self.assertEquals(y.dt, None)
+ self.assertEquals(y.t, None)
+
+ def test_undefined(self):
+ from django.db import models
+ from django.db.models import fields
+
+ class UndefinedClass(models.Model):
+ pass
+
+ alias = self.adapter.DjangoClassAlias(UndefinedClass, None)
+
+ x = UndefinedClass()
+
+ alias.applyAttributes(x, {
+ 'id': pyamf.Undefined
+ })
+
+ self.assertEquals(x.id, fields.NOT_PROVIDED)
+
+ x.id = fields.NOT_PROVIDED
+
+ sa, da = alias.getEncodableAttributes(x)
+
+ self.assertEquals(da, None)
+ self.assertEquals(sa, {'id': pyamf.Undefined})
+
+ def test_non_field_prop(self):
+ from django.db import models
+
+ class Book(models.Model):
+ def _get_number_of_odd_pages(self):
+ return 234
+
+ # note the lack of a setter callable ..
+ numberOfOddPages = property(_get_number_of_odd_pages)
+
+ alias = self.adapter.DjangoClassAlias(Book, 'Book')
+
+ x = Book()
+
+ self.assertEquals(alias.getEncodableAttributes(x), (
+ {'id': None},
+ {'numberOfOddPages': 234}
+ ))
+
+ # now we test sending the numberOfOddPages attribute
+ alias.applyAttributes(x, {'numberOfOddPages': 24, 'id': None})
+
+ # test it hasn't been set
+ self.assertEquals(x.numberOfOddPages, 234)
+
+ def test_dynamic(self):
+ """
+ Test for dynamic property encoding.
+ """
+ from django.db import models
+
+ class Foo(models.Model):
+ pass
+
+ alias = self.adapter.DjangoClassAlias(Foo, 'Book')
+
+ x = Foo()
+ x.spam = 'eggs'
+
+ self.assertEquals(alias.getEncodableAttributes(x), (
+ {'id': None},
+ {'spam': 'eggs'}
+ ))
+
+ # now we test sending the numberOfOddPages attribute
+ alias.applyAttributes(x, {'spam': 'foo', 'id': None})
+
+ # test it has been set
+ self.assertEquals(x.spam, 'foo')
+
+
+class ForeignKeyTestCase(ModelsBaseTestCase):
+ def test_one_to_many(self):
+ from django.db import models
+
+ class Reporter(models.Model):
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
+ email = models.EmailField()
+
+ def __unicode__(self):
+ return u"%s %s" % (self.first_name, self.last_name)
+
+ class Article(models.Model):
+ headline = models.CharField(max_length=100)
+ pub_date = models.DateField()
+ reporter = models.ForeignKey(Reporter)
+
+ def __unicode__(self):
+ return self.headline
+
+ self.resetDB()
+
+ # initialise the db ..
+ r = Reporter(first_name='John', last_name='Smith', email='john@example.com')
+ r.save()
+
+ r2 = Reporter(first_name='Paul', last_name='Jones', \
email='paul@example.com') + r2.save()
+
+ a = Article(headline="This is a test", pub_date=datetime.date(2005, 7, 27), \
reporter=r) + a.save()
+
+ self.assertEquals(a.id, 1)
+
+ del a
+
+ a = Article.objects.filter(pk=1)[0]
+
+ self.assertFalse('_reporter_cache' in a.__dict__)
+ a.reporter
+ self.assertTrue('_reporter_cache' in a.__dict__)
+
+ del a
+
+ a = Article.objects.filter(pk=1)[0]
+ alias = self.adapter.DjangoClassAlias(Article, defer=True)
+
+ self.assertFalse(hasattr(alias, 'fields'))
+ sa, da = alias.getEncodableAttributes(a)
+ self.assertEquals(sa, {
+ 'headline': u'This is a test',
+ 'pub_date': datetime.datetime(2005, 7, 27, 0, 0),
+ 'id': 1,
+ })
+ # note that the reporter attribute does not exist.
+ self.assertEquals(da, None)
+
+ self.assertFalse('_reporter_cache' in a.__dict__)
+ self.assertEquals(pyamf.encode(a, encoding=pyamf.AMF3).getvalue(),
+ '\n;\x01\x11headline\x05id\x11pub_date\x06\x1dThis is a test\x04'
+ '\x01\x08\x01BpUYj@\x00\x00\x01')
+
+ del a
+
+ # now with select_related to pull in the reporter object
+ a = Article.objects.select_related().filter(pk=1)[0]
+
+ alias = self.adapter.DjangoClassAlias(Article, defer=True)
+
+ self.assertFalse(hasattr(alias, 'fields'))
+ self.assertEquals(alias.getEncodableAttributes(a), ({
+ 'headline': u'This is a test',
+ 'pub_date': datetime.datetime(2005, 7, 27, 0, 0),
+ 'id': 1,
+ },
+ {
+ 'reporter': r,
+ }))
+
+ self.assertTrue('_reporter_cache' in a.__dict__)
+ self.assertEquals(pyamf.encode(a, encoding=pyamf.AMF3).getvalue(),
+ '\n;\x01\x11headline\x05id\x11pub_date\x06\x1dThis is a test\x04'
+ '\x01\x08\x01BpUYj@\x00\x00\x11reporter\nK\x01\x0bemail\x15'
+ 'first_name\x02\x13last_name\x06!john@example.com\x06\tJohn\x04'
+ '\x01\x06\x0bSmith\x01\x01')
+
+ def test_many_to_many(self):
+ from django.db import models
+
+ class Publication(models.Model):
+ title = models.CharField(max_length=30)
+
+ def __unicode__(self):
+ return self.title
+
+ class Meta:
+ ordering = ('title',)
+
+ class Article2(models.Model):
+ headline = models.CharField(max_length=100)
+ publications = models.ManyToManyField(Publication)
+
+ def __unicode__(self):
+ return self.headline
+
+ class Meta:
+ ordering = ('headline',)
+
+ self.resetDB()
+
+ # install some test data - taken from
+ # http://www.djangoproject.com/documentation/models/many_to_many/
+ p1 = Publication(id=None, title='The Python Journal')
+ p1.save()
+ p2 = Publication(id=None, title='Science News')
+ p2.save()
+ p3 = Publication(id=None, title='Science Weekly')
+ p3.save()
+
+ # Create an Article.
+ a1 = Article2(id=None, headline='Django lets you build Web apps easily')
+ a1.save()
+ self.assertEquals(a1.id, 1)
+
+ # Associate the Article with a Publication.
+ a1.publications.add(p1)
+
+ pub_alias = self.adapter.DjangoClassAlias(Publication, None)
+ art_alias = self.adapter.DjangoClassAlias(Article2, None)
+
+ test_publication = Publication.objects.filter(pk=1)[0]
+ test_article = Article2.objects.filter(pk=1)[0]
+
+ sa, da = pub_alias.getEncodableAttributes(test_publication)
+ self.assertEquals(sa, {'id': 1, 'title': u'The Python Journal'})
+ self.assertEquals(da, None)
+
+ sa, da = art_alias.getEncodableAttributes(test_article)
+ self.assertEquals(sa, {
+ 'headline': u'Django lets you build Web apps easily',
+ 'id': 1,
+ 'publications': [p1]
+ })
+ self.assertEquals(da, None)
+
+ x = Article2()
+ art_alias.applyAttributes(x, {
+ 'headline': u'Test',
+ 'id': 1,
+ 'publications': [p1]
+ })
+
+ self.assertEquals(x.headline, u'Test')
+ self.assertEquals(x.id, 1)
+
+ p = x.publications.all()
+
+ self.assertEquals(len(p), 1)
+ self.assertEquals(p[0], p1)
+
+ def test_nullable_foreign_keys(self):
+ from django.db import models
+
+ class FooBar(models.Model):
+ pass
+
+ class NullForeignKey(models.Model):
+ foobar = models.ForeignKey(FooBar, null=True)
+
+ class BlankForeignKey(models.Model):
+ foobar = models.ForeignKey(FooBar, blank=True)
+
+ self.resetDB()
+
+ x = FooBar()
+ x.save()
+
+ nfk_alias = self.adapter.DjangoClassAlias(NullForeignKey, None)
+ bfk_alias = self.adapter.DjangoClassAlias(BlankForeignKey, None)
+
+ nfk = NullForeignKey()
+
+ sa, da = nfk_alias.getEncodableAttributes(nfk)
+
+ self.assertEquals(sa, {'id': None})
+ self.assertEquals(da, None)
+
+ def test_static_relation(self):
+ """
+ @see: #693
+ """
+ from django.db import models
+ from pyamf import util
+
+ class Gak(models.Model):
+ pass
+
+ class Baz(models.Model):
+ gak = models.ForeignKey(Gak)
+
+ class __amf__:
+ static = ('gak',)
+
+ self.resetDB()
+
+ alias = self.adapter.DjangoClassAlias(Baz, **util.get_class_meta(Baz))
+
+ alias.compile()
+
+ self.assertTrue('gak' in alias.relations)
+ self.assertTrue('gak' in alias.decodable_properties)
+ self.assertTrue('gak' in alias.static_attrs)
+
+ x = Baz()
+
+ alias.getDecodableAttributes(x, {'id': None, 'gak': 'foo'})
+
+
+class I18NTestCase(ModelsBaseTestCase):
+ def test_encode(self):
+ from django.utils.translation import ugettext_lazy
+
+ self.assertEquals(pyamf.encode(ugettext_lazy('Hello')).getvalue(),
+ '\x02\x00\x05Hello')
+
+
+class PKTestCase(ModelsBaseTestCase):
+ """
+ See ticket #599 for this. Check to make sure that django pk fields
+ are set first
+ """
+
+ def test_behaviour(self):
+ from django.db import models
+
+ class Publication(models.Model):
+ title = models.CharField(max_length=30)
+
+ def __unicode__(self):
+ return self.title
+
+ class Meta:
+ ordering = ('title',)
+
+ class Article2(models.Model):
+ headline = models.CharField(max_length=100)
+ publications = models.ManyToManyField(Publication)
+
+ def __unicode__(self):
+ return self.headline
+
+ class Meta:
+ ordering = ('headline',)
+
+ self.resetDB()
+
+ p = Publication(id=None, title='The Python Journal')
+ a = Article2(id=None, headline='Django lets you build Web apps easily')
+
+ # Associate the Article with a Publication.
+ self.assertRaises(ValueError, lambda a, p: a.publications.add(p), a, p)
+
+ p.save()
+ a.save()
+
+ self.assertEquals(a.id, 2)
+
+ article_alias = self.adapter.DjangoClassAlias(Article2, None)
+ x = Article2()
+
+ article_alias.applyAttributes(x, {
+ 'headline': 'Foo bar!',
+ 'id': 2,
+ 'publications': [p]
+ })
+
+ def test_none(self):
+ """
+ See #556. Make sure that PK fields with a value of 0 are actually set
+ to C{None}.
+ """
+ from django.db import models
+
+ class Foo(models.Model):
+ pass
+
+ self.resetDB()
+
+ alias = self.adapter.DjangoClassAlias(Foo, None)
+
+ x = Foo()
+
+ self.assertEquals(x.id, None)
+
+ alias.applyAttributes(x, {
+ 'id': 0
+ })
+
+ self.assertEquals(x.id, None)
+
+ def test_no_pk(self):
+ """
+ Ensure that Models without a primary key are correctly serialized.
+ See #691.
+ """
+ from django.db import models
+
+ class NotSaved(models.Model):
+ name = models.CharField(max_length=100)
+
+ instances = [NotSaved(name="a"), NotSaved(name="b")]
+ encoded = pyamf.encode(instances, encoding=pyamf.AMF3).getvalue()
+ decoded = pyamf.get_decoder(pyamf.AMF3, encoded).readElement()
+ self.assertEquals(decoded[0]['name'], 'a')
+ self.assertEquals(decoded[1]['name'], 'b')
+
+
+class ModelInheritanceTestCase(ModelsBaseTestCase):
+ """
+ Tests for L{Django model \
inheritance<http://docs.djangoproject.com/en/dev/topics/db/models/#model-inheritance>}
+ """
+
+ def test_abstract(self):
+ from django.db import models
+
+ class CommonInfo(models.Model):
+ name = models.CharField(max_length=100)
+ age = models.PositiveIntegerField()
+
+ class Meta:
+ abstract = True
+
+ class Student(CommonInfo):
+ home_group = models.CharField(max_length=5)
+
+ self.resetDB()
+
+ alias = self.adapter.DjangoClassAlias(Student)
+
+ x = Student()
+
+ sa, da = alias.getEncodableAttributes(x)
+
+ self.assertEquals(sa, {
+ 'age': None,
+ 'home_group': '',
+ 'id': None,
+ 'name': ''
+ })
+
+ self.assertEquals(da, None)
+
+ def test_concrete(self):
+ from django.db import models
+
+ class Place(models.Model):
+ name = models.CharField(max_length=50)
+ address = models.CharField(max_length=80)
+
+ class Restaurant(Place):
+ serves_hot_dogs = models.BooleanField()
+ serves_pizza = models.BooleanField()
+
+ self.resetDB()
+
+ alias = self.adapter.DjangoClassAlias(Place)
+ x = Place()
+
+ sa, da = alias.getEncodableAttributes(x)
+
+ self.assertEquals(sa, {
+ 'id': None,
+ 'name': '',
+ 'address': ''
+ })
+
+ self.assertEquals(da, None)
+
+ alias = self.adapter.DjangoClassAlias(Restaurant)
+ x = Restaurant()
+
+ sa, da = alias.getEncodableAttributes(x)
+
+ self.assertEquals(sa, {
+ 'id': None,
+ 'name': '',
+ 'address': '',
+ 'serves_hot_dogs': False,
+ 'serves_pizza': False
+ })
+
+ self.assertEquals(da, None)
+
+
+class MockFile(object):
+ """
+ mock for L{django.core.files.base.File}
+ """
+
+ def chunks(self):
+ return []
+
+ def __len__(self):
+ return 0
+
+ def read(self, n):
+ return ''
+
+
+class FieldsTestCase(ModelsBaseTestCase):
+ """
+ Tests for L{fields}
+ """
+
+ def tearDown(self):
+ ModelsBaseTestCase.tearDown(self)
+
+ try:
+ os.unlink(os.path.join(os.getcwd(), 'foo'))
+ except OSError:
+ raise
+ pass
+
+ def test_file(self):
+ from django.db import models
+
+ self.executed = False
+
+ def get_studio_watermark(*args, **kwargs):
+ self.executed = True
+
+ return 'foo'
+
+ class Image(models.Model):
+ file = models.FileField(upload_to=get_studio_watermark)
+ text = models.CharField(max_length=64)
+
+ self.resetDB()
+
+ alias = self.adapter.DjangoClassAlias(Image)
+
+ i = Image()
+ i.file.save('bar', MockFile())
+
+ i.save()
+
+ sa, da = alias.getEncodableAttributes(i)
+
+ self.assertEquals(sa, {'text': '', 'id': 1, 'file': u'foo'})
+ self.assertEquals(da, None)
+ self.assertTrue(self.executed)
+
+ attrs = alias.getDecodableAttributes(i, sa)
+
+ self.assertEquals(attrs, {'text': ''})
+
+
+class ImageTestCase(ModelsBaseTestCase):
+ """
+ Tests for L{fields}
+ """
+
+ def test_image(self):
+ from django.db import models
+
+ self.executed = False
+
+ def get_studio_watermark(*args, **kwargs):
+ self.executed = True
+
+ return 'foo'
+
+ class Profile(models.Model):
+ file = models.ImageField(upload_to=get_studio_watermark)
+ text = models.CharField(max_length=64)
+
+ self.resetDB()
+
+ alias = self.adapter.DjangoClassAlias(Profile)
+
+ i = Profile()
+ i.file.save('bar', MockFile())
+
+ i.save()
+
+ sa, da = alias.getEncodableAttributes(i)
+
+ self.assertEquals(sa, {'text': '', 'id': 1, 'file': u'foo_'})
+ self.assertEquals(da, None)
+ self.assertTrue(self.executed)
+
+ attrs = alias.getDecodableAttributes(i, sa)
+
+ self.assertEquals(attrs, {'text': ''})
+
+
+class ReferenceTestCase(ModelsBaseTestCase):
+ """
+ Test case to make sure that the same object from the database is encoded
+ by reference.
+ """
+
+ def setUp(self):
+ ModelsBaseTestCase.setUp(self)
+
+ from django.db import models
+
+ class ParentReference(models.Model):
+ name = models.CharField(max_length=100)
+ bar = models.ForeignKey('ChildReference', null=True)
+
+ class ChildReference(models.Model):
+ name = models.CharField(max_length=100)
+ foo = models.ForeignKey(ParentReference)
+
+ self.ParentReference = ParentReference
+ self.ChildReference = ChildReference
+
+ self.resetDB()
+
+ def tearDown(self):
+ ModelsBaseTestCase.tearDown(self)
+
+ def test_not_referenced(self):
+ """
+ Test to ensure that we observe the correct behaviour in the Django
+ ORM.
+ """
+ f = self.ParentReference()
+ f.name = 'foo'
+
+ b = self.ChildReference()
+ b.name = 'bar'
+
+ f.save()
+ b.foo = f
+ b.save()
+ f.bar = b
+ f.save()
+
+ self.assertEquals(f.id, 1)
+ foo = self.ParentReference.objects.select_related().get(id=1)
+
+ self.assertFalse(foo.bar.foo is foo)
+
+ def test_referenced_encode(self):
+ f = self.ParentReference()
+ f.name = 'foo'
+
+ b = self.ChildReference()
+ b.name = 'bar'
+
+ f.save()
+ b.foo = f
+ b.save()
+ f.bar = b
+ f.save()
+
+ self.assertEquals(f.id, 2)
+ foo = self.ParentReference.objects.select_related().get(id=2)
+
+ # ensure the referenced attribute resolves
+ foo.bar.foo
+
+ self.assertEquals(pyamf.encode(foo).getvalue(), '\x03\x00\x02id\x00'
+ '@\x00\x00\x00\x00\x00\x00\x00\x00\x04name\x02\x00\x03foo\x00'
+ '\x03bar\x03\x00\x02id\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x04na'
+ 'me\x02\x00\x03bar\x00\x03foo\x07\x00\x00\x00\x00\t\x00\x00\t')
+
+
+def suite():
+ suite = unittest.TestSuite()
+
+ try:
+ import django
+ except ImportError:
+ return suite
+
+ test_cases = [
+ TypeMapTestCase,
+ ClassAliasTestCase,
+ ForeignKeyTestCase,
+ I18NTestCase,
+ PKTestCase,
+ ModelInheritanceTestCase,
+ FieldsTestCase,
+ ReferenceTestCase
+ ]
+
+ try:
+ import PIL
+ except:
+ pass
+ else:
+ test_cases.append(ImageTestCase)
+
+ for tc in test_cases:
+ suite.addTest(unittest.makeSuite(tc))
+
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
_______________________________________________
PyAMF commits mailing list - commits@pyamf.org
http://lists.pyamf.org/mailman/listinfo/commits
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic