Giter VIP home page Giter VIP logo

django-polymorphic's Introduction

https://readthedocs.org/projects/django-polymorphic/badge/?version=stable Jazzband

Polymorphic Models for Django

Django-polymorphic simplifies using inherited models in Django projects. When a query is made at the base model, the inherited model classes are returned.

When we store models that inherit from a Project model...

>>> Project.objects.create(topic="Department Party")
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")

...and want to retrieve all our projects, the subclassed models are returned!

>>> Project.objects.all()
[ <Project:         id 1, topic "Department Party">,
  <ArtProject:      id 2, topic "Painting with Tim", artist "T. Turner">,
  <ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]

Using vanilla Django, we get the base class objects, which is rarely what we wanted:

>>> Project.objects.all()
[ <Project: id 1, topic "Department Party">,
  <Project: id 2, topic "Painting with Tim">,
  <Project: id 3, topic "Swallow Aerodynamics"> ]

This also works when the polymorphic model is accessed via ForeignKeys, ManyToManyFields or OneToOneFields.

Features

  • Full admin integration.
  • ORM integration:
    • support for ForeignKey, ManyToManyField, OneToOneField descriptors.
    • Filtering/ordering of inherited models (ArtProject___artist).
    • Filtering model types: instance_of(...) and not_instance_of(...)
    • Combining querysets of different models (qs3 = qs1 | qs2)
    • Support for custom user-defined managers.
  • Uses the minimum amount of queries needed to fetch the inherited models.
  • Disabling polymorphic behavior when needed.

Note: While django-polymorphic makes subclassed models easy to use in Django, we still encourage to use them with caution. Each subclassed model will require Django to perform an INNER JOIN to fetch the model fields from the database. While taking this in mind, there are valid reasons for using subclassed models. That's what this library is designed for!

The current release of django-polymorphic supports Django 2.2 - 4.0 on Python 3.6+.

For more information, see the documentation at Read the Docs.

Installation

Install using pip...

$ pip install django-polymorphic

At the moment we have an unoffical version (4.0.0a). While we wait to gain access to pip. If you want to use the latest version (which works for Django >4.0.0). You can install it using

pip install git+https://github.com/jazzband/[email protected]#egg=django-polymorphic

License

Django-polymorphic uses the same license as Django (BSD-like).

django-polymorphic's People

Contributors

adamdonna avatar ajmatsick avatar akx avatar alexander-alvarez avatar bconstantin avatar benkonrath avatar bertrandbordage avatar chrisglass avatar dependabot[bot] avatar fdintino avatar floppya avatar gannettchad avatar gavinwahl avatar hottwaj avatar j-antunes avatar jedediah avatar jleclanche avatar jmfederico avatar jonashaag avatar kronuz avatar marksweb avatar meshy avatar ojii avatar petrdlouhy avatar pre-commit-ci[bot] avatar rickstc avatar skirsdeda avatar un-def avatar vdboor avatar yakky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-polymorphic's Issues

Abstract base PolymorphicModels cause field name clashes

In a scenario where your base PolymorphicModel is abstract (for example, when you don't need a "simple" Project, a ForeignKey on it will cause field name clashes:

class BaseProject(PolymorphicModel):
    owner = models.ForeignKey(User, related_name='projects')
    class Meta:
        abstract = True

class ArtProject(BaseProject):
    # add some fields
    pass

class ResearchProject(BaseProject):
    # add some fields
    pass

This will cause the following errors (when running ./manage.py validate):

app.artproject: Accessor for field 'owner' clashes with related field 'User.projects'. Add a related_name argument to the definition for 'owner'.
app.artproject: Reverse query name for field 'owner' clashes with related field 'User.projects'. Add a related_name argument to the definition for 'owner'.
app.artproject: Accessor for field 'owner' clashes with related field 'User.projects'. Add a related_name argument to the definition for 'owner'.
app.artproject: Reverse query name for field 'owner' clashes with related field 'User.projects'. Add a related_name argument to the definition for 'owner'.

Same output for ResearchProject, the error gets printed twice for every model.

Is this by design or is a fix possible?
Some kind of workaround at the moment is defining the base model as not abstract, obviously at the cost of SQL overhead/performance.

Polymorphic and multi database router

I encounter a problem when using django-polymorphic with the built-in django database router:

the method get_real_instance_class of PolymorphicModel retreives a ContentType without defining the database to use (ContentType.objects.get_for_id(...)).

Does polymorphic support multi database?

Simplify add_type_form.html

Rendering is broken when using django-grappelli:

Current grappelli rendering

Simplifying add_type_form.html fixes it. With only this and nothing else:

{% extends "admin/change_form.html" %}

{% block submit_buttons_bottom %}
  {% include 'admin/submit_line.html' with show_save=True %}
{% endblock %}

we get:
Expected grappelli rendering

It also works perfectly without grappelli:
Expected default rendering

If this change is OK, I'll make a pull request.

Compatibility with 1.7

Currently I'm getting deprecation warnings, just as a heads up:
/home/taishi/.virtualenvs/altcoinbookie/lib/python3.3/site-packages/polymorphic/manager.py:11: DeprecationWarning: PolymorphicManager.get_query_set method should be renamed get_queryset.
class PolymorphicManager(models.Manager):

/home/taishi/.virtualenvs/altcoinbookie/lib/python3.3/site-packages/taggit/managers.py:279: DeprecationWarning: _TaggableManager.get_prefetch_query_set method should be renamed get_prefetch_queryset.
class _TaggableManager(models.Manager):

/home/taishi/.virtualenvs/altcoinbookie/lib/python3.3/site-packages/Django-1.7a2-py3.3.egg/django/forms/widgets.py:142: DeprecationWarning: PolymorphicParentModelAdmin.queryset method should be renamed get_queryset.
.new(mcs, name, bases, attrs))

OneToOneField cannot be primary key

This example

class Account(PolymorphicModel):
    user = models.OneToOneField(User, primary_key=True)

fails with Caught AttributeError while rendering: type object 'Account' has no attribute 'polymorphic_primary_key_name'

2c47db8 introduced a new way of determining pk's which specifically excludes OneToOneField fields: if f.primary_key and type(f)!=models.OneToOneField:

This issue was reported downstream 2 years ago at bconstantin#17.

Why exclude OneToOneField's? The tests continue to pass after removing this restriction.

Custom primary key confusion

With a set of models like:

Owner(polymorphic), has many Projects
FooOwner(Owner), has custom primary key

Project(polymorphic), has many Owners
BarProject(Project), has custom primary key

performing barproject.owners.add(FooOwner()) seemed to have no effect; afterwards, barproject.owners.all() would be empty.

When I went to write a test case to recreate, I noticed I was triggering this line in my test output. I then removed the custom primary keys from FooOwner and BarProject, which fixed the problem.

Is this a known issue? The closest mention I saw in the docs was about unique primary keys in calls to extra(), but the test output seems to suggest somebody is aware of this.

url reverse

Hi, I use uuid as PrimaryKey. And meet issue with url patterns

Hard coded \d+

def get_urls(self):
    ...
    new_change_url = url(r'^(\d+)/$', self.admin_site.admin_view(self.change_view), name='{0}_{1}_change'.format(*info))
    ...

Maybe it is possible to add new attr to PolymorphicParentModelAdmin like

pk_regex = '\d+'

regards, Andrew

Admin list page doesn't resolve to child classes

I've just set up admin support for a bunch of classes, and it's very slick, but in the list view it's using only the parent class, so the unicode method which relies upon a property set in a child class does not change.

What can I do to have the resolved child class to access?

Automatic instance_of() on Model.objects ?

Hi !

When you have models like:

class ModelA(PolymorphicModel):
    pass

class ModelB(ModelA):
    pass

class ModelC(ModelA):
    pass

It would be nice to be able to do:

>>> ModelB.objects.all()
[<only ModelB instances, not ModelC>]

without the addition of instance_of everytime.

I have a simple solution, and am looking for comments/other ways to do this:

class InstanceOfPolymorphicManager(PolymorphicManager):
    def get_query_set(self):
        queryset = super(InstancePolymorphicManager, self).get_query_set()
        return queryset.instance_of(self.model)

class ModelA(PolymorphicModel):
    objects = InstanceOfPolymorphicManager()
    # no need to repeat it in subclasses, the manager will pick the proper model to use in "instance_of(self.model)

Incompatibility with johnny-cache

Using django_polymorphic with johnny-cache (https://bitbucket.org/jmoiron/johnny-cache) enabled results in an error:

(In this case the FilerFile object)
Caught AttributeError while rendering: type object 'FilerFile' has no attribute 'base_objects',

/home/cmstest/djangocms/lib/python2.6/site-packages/polymorphic/query.py in _get_real_instances
qs = modelclass.base_objects.filter(pkin=idlist) # use pkin instead ####

Add Django 1.5 support

From what I've seen, django-polymorphic doesn't work with Django 1.5. I guess the QuerySet API's have changed a bit?

_default_manager can't be PolymorphicManager when adding user_managers

Hi,

If we define a new custom manager on a model as defined in the doc: https://django-polymorphic.readthedocs.org/en/latest/managers.html#using-a-custom-manager, it is not possible to keep the PolymorphicManager() as _default_manager because the classmethod get_first_user_defined_manager() skip the manager if its type is PolymorphicManager:

for key, val in new_class.dict.items():
if isinstance(val, ManagerDescriptor):
val = val.manager
if not isinstance(val, PolymorphicManager) or type(val) is PolymorphicManager:
continue

According to the following example of the doc:
class Project(PolymorphicModel):
objects = PolymorphicManager() # add the default polymorphic manager first
objects_ordered = TimeOrderedManager() # then add your own manager
start_date = DateTimeField() # project start is this date/time

It means that _default_manager will be filled with TimeOrderedManager().
Is it normal ?

If the Project class doesn't redefined objects = PolymorphicManager(), I agree with this behavior, but if objects = PolymorphicManager() is redefined in the class, I think that it means 'use PolymorphicManager as _default_manager'.

Issues with multiple inheritance and sequence nos

Hello. I am using multiple inheritance as follows:

class A(PolymorphicModel):
..

class B(PolymorphicModel):
..

class C(PolymorphicModel):
..

class B_C(B, C):
..

class A_BC(A, B_C)
..

The issue is simple:
when saving an A_BC object, the DB table for A would determine the pk of the A_BC object and B and C are set to share the same PK.
This can cause 2 sorts of issues:

  • when the DB's SEQUENCE counters for B and/or C are lower than that of A, this is likely to raise an error at some point when a B or C objet is stored with the sequence number of the A_BC object
  • when the same counters are higher than A, an existing B and/or C object is likely to be overridden

One workaround I've tried to implement is a Mixin that would fake the save_base(cls=B...) and save_base(cls=C...) which happen in the process to believe that the object has no PK, but it has a side effect on my setup and makes it non-functional (C indeed has a OneToOne to B and B to A, which I have populated in the classes save_base() but work no longer with my mixin).
Do you see any chance to make the B- and C- parts of such a model obey their normal SEQUENCE counters?

Old Docs from Wayback machine

Polymorphic Inheritance for Django Models

Table of Contents

Installation / Testing
Defining Polymorphic Models
Using Polymorphic Models
Custom Managers, Querysets & Inheritance
Performance Considerations
Possible Optimizations
Restrictions & Caveats
Project Status
Links
Installation / Testing

Requirements

Django 1.1 (or later) and Python 2.4 / 2.5 / 2.6. This code has been tested on Django 1.1.1 / 1.2 beta and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.

Testing

The repository (or tar file) contains a complete Django project that may be used for tests or experiments, without any installation needed.

To run the included test suite, execute:

./manage test
The management command pcmd.py in the app pexp (Polymorphic EXPerimenting) can be used for experiments - modify this file (pexp/management/commands/pcmd.py) to your liking, then run:

./manage syncdb # db is created in /var/tmp/... (settings.py)
./manage pcmd
Installation

In the directory "django_polymorphic", execute sudo python setup.py install.

Alternatively you can simply copy the polymorphic directory (under "django_polymorphic") into your Django project dir.

If you want to use the management command polymorphic_dumpdata, then you need to add polymorphic to your INSTALLED_APPS setting. This is also needed if you want to run the test cases in polymorphic/tests.py.

In any case, Django's ContentType framework (django.contrib.contenttypes) needs to be listed in INSTALLED_APPS (usually it already is).

Defining Polymorphic Models

To make models polymorphic, use PolymorphicModel instead of Django's models.Model as the superclass of your base model. All models inheriting from your base class will be polymorphic as well:

from polymorphic import PolymorphicModel

class ModelA(PolymorphicModel):
field1 = models.CharField(max_length=10)

class ModelB(ModelA):
field2 = models.CharField(max_length=10)

class ModelC(ModelB):
field3 = models.CharField(max_length=10)
Using Polymorphic Models

Most of Django's standard ORM functionality is available and works as expected:

Create some objects

ModelA.objects.create(field1='A1')
ModelB.objects.create(field1='B1', field2='B2')
ModelC.objects.create(field1='C1', field2='C2', field3='C3')
Query results are polymorphic

ModelA.objects.all()
.
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Filtering for classes (equivalent to python's isinstance() ):

ModelA.objects.instance_of(ModelB)
.
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
In general, including or excluding parts of the inheritance tree:

ModelA.objects.instance_of(ModelB [, ModelC ...])
ModelA.objects.not_instance_of(ModelB [, ModelC ...])
Polymorphic filtering (for fields in derived classes)

For example, cherrypicking objects from multiple derived classes anywhere in the inheritance tree, using Q objects (with the syntax: exact model name + three _ + field name):

ModelA.objects.filter( Q(ModelB___field2 = 'B2') | Q(ModelC___field3 = 'C3') )
.
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Combining Querysets of different types/models

Querysets may now be regarded as object containers that allow the aggregation of different object types - very similar to python lists (as long as the objects are accessed through the manager of a common base class):

Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
.
[ <ModelX: id 1, field_x (CharField)>,
<ModelY: id 2, field_y (CharField)> ]
Using Third Party Models (without modifying them)

Third party models can be used as polymorphic models without restrictions by subclassing them. E.g. using a third party model as the root of a polymorphic inheritance tree:

from thirdparty import ThirdPartyModel

class MyThirdPartyModel(PolymorhpicModel, ThirdPartyModel):
pass # or add fields
Or instead integrating the third party model anywhere into an existing polymorphic inheritance tree:

class MyModel(SomePolymorphicModel):
my_field = models.CharField(max_length=10)

class MyModelWithThirdParty(MyModel, ThirdPartyModel):
pass # or add fields
ManyToManyField, ForeignKey, OneToOneField

Relationship fields referring to polymorphic models work as expected: like polymorphic querysets they now always return the referred objects with the same type/class these were created and saved as.

E.g., if in your model you define:

field1 = OneToOneField(ModelA)
then field1 may now also refer to objects of type ModelB or ModelC.

A ManyToManyField example:

The model holding the relation may be any kind of model, polymorphic or not

class RelatingModel(models.Model):
many2many = models.ManyToManyField('ModelA') # ManyToMany relation to a polymorphic model

o=RelatingModel.objects.create()
o.many2many.add(ModelA.objects.get(id=1))
o.many2many.add(ModelB.objects.get(id=2))
o.many2many.add(ModelC.objects.get(id=3))

o.many2many.all()
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
Non-Polymorphic Queries

ModelA.base_objects.all()
.
[ <ModelA: id 1, field1 (CharField)>,
<ModelA: id 2, field1 (CharField)>,
<ModelA: id 3, field1 (CharField)> ]
Each polymorphic model has 'base_objects' defined as a normal Django manager. Of course, arbitrary custom managers may be added to the models as well.

More Queryset Methods

annotate() and aggregate() work just as usual, with the addition that the ModelX___field syntax can be used for the keyword arguments (but not for the non-keyword arguments).
order_by() now similarly supports the ModelX___field syntax for specifying ordering through a field in a submodel.
distinct() works as expected. It only regards the fields of the base class, but this should never make a difference.
select_related() works just as usual, but it can not (yet) be used to select relations in derived models (like ModelA.objects.select_related('ModelC___fieldxy') )
extra() by default works exactly like the original version, with the resulting queryset not being polymorphic. There is experimental support for a polymorphic extra() via the keyword argument polymorphic=True (only the where and order_by arguments of extra() should be used then).
values() & values_list() currently do not return polymorphic results. This may change in the future however. If you want to use these methods now, it's best if you use Model.base_objects.values... as this is guaranteed to not change.
defer() and only() are not yet supported (support will be added in the future).
manage.py dumpdata

Django's standard dumpdata command requires non-polymorphic behaviour from the querysets it uses and produces incomplete results with polymorphic models. Django_polymorphic includes a slightly modified version, named polymorphic_dumpdata that fixes this. Just use this command instead of Django's (see "installation/testing").

Please note that there are problems using ContentType together with Django's seralisation or fixtures (and all polymorphic models use ContentType). This issue seems to be resolved with Django 1.2 (changeset 11863): http://code.djangoproject.com/ticket/7052

Custom Managers, Querysets & Inheritance

Using a Custom Manager

For creating a custom polymorphic manager class, derive your manager from PolymorphicManager instead of models.Manager. In your model class, explicitly add the default manager first, and then your custom manager:

from polymorphic import PolymorphicModel, PolymorphicManager

class MyOrderedManager(PolymorphicManager):
def get_query_set(self):
return super(MyOrderedManager,self).get_query_set().order_by('some_field')

class MyModel(PolymorphicModel):
objects = PolymorphicManager() # add the default polymorphic manager first
ordered_objects = MyOrderedManager() # then add your own manager
The first manager defined ('objects' in the example) is used by Django as automatic manager for several purposes, including accessing related objects. It must not filter objects and it's safest to use the plain PolymorphicManager here.

Manager Inheritance

Polymorphic models inherit/propagate all managers from their base models, as long as these are polymorphic. This means that all managers defined in polymorphic base models work just the same as if they were defined in the new model.

An example (inheriting from MyModel above):

class MyModel2(MyModel):
pass

Managers inherited from MyModel:

the regular 'objects' manager and the custom 'ordered_objects' manager

MyModel2.objects.all()
MyModel2.ordered_objects.all()
Using a Custom Queryset Class

The PolymorphicManager class accepts one initialization argument, which is the queryset class the manager should use. A custom custom queryset class can be defined and used like this:

from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet

class MyQuerySet(PolymorphicQuerySet):
def my_queryset_method(...):
...

class MyModel(PolymorphicModel):
my_objects=PolymorphicManager(MyQuerySet)
...
Performance Considerations

The current implementation is pretty simple and does not use any custom SQL - it is purely based on the Django ORM. Right now the query

result_objects = list( ModelA.objects.filter(...) )
performs one SQL query to retrieve ModelA objects and one additional query for each unique derived class occurring in result_objects. The best case for retrieving 100 objects is 1 db query if all are class ModelA. If 50 objects are ModelA and 50 are ModelB, then two queries are executed. If result_objects contains only the base model type (ModelA), the polymorphic models are just as efficient as plain Django models (in terms of executed queries). The pathological worst case is 101 db queries if result_objects contains 100 different object types (with all of them subclasses of ModelA).

Performance ist relative: when Django users create their own polymorphic ad-hoc solution (without a tool like django_polymorphic), this usually results in a variation of

result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
which has really bad performance. Relative to this, the performance of the current django_polymorphic is pretty good. It may well be efficient enough for the majority of use cases.

Chunking: The implementation always requests objects in chunks of size Polymorphic_QuerySet_objects_per_request. This limits the complexity/duration for each query, including the pathological cases.

Possible Optimizations

PolymorphicQuerySet can be optimized to require only one SQL query for the queryset evaluation and retrieval of all objects.

Basically, what ist needed is a possibility to pull in the fields from all relevant sub-models with one SQL query. However, some deeper digging into the Django database layer will be required in order to make this happen.

A viable option might be to get the SQL query from the QuerySet (probably from django.db.models.SQL.compiler.SQLCompiler.as_sql), making sure that all necessary joins are done, and then doing a custom SQL request from there (like in SQLCompiler.execute_sql).

An optimized version could fall back to the current ORM-only implementation for all non-SQL databases.

SQL Complexity

With only one SQL query, one SQL join for each possible subclass would be needed (BaseModel.subclasses(), recursively). With two SQL queries, the number of joins could be reduced to the number of actuallly occurring subclasses in the result. A final implementation might want to use one query only if the number of possible subclasses (and therefore joins) is not too large, and two queries otherwise (using the first query to determine the actually occurring subclasses, reducing the number of joins for the second).

A relatively large number of joins may be needed in both cases, which raises concerns regarding the efficiency of these database queries. It is currently unclear however, how many model classes will actually be involved in typical use cases - the total number of classes in the inheritance tree as well as the number of distinct classes in query results. It may well turn out that the increased number of joins is no problem for the DBMS in all realistic use cases. Alternatively, if the SQL query execution time is significantly longer even in common use cases, this may still be acceptable in exchange for the added functionality.

In General

Let's not forget that all of the above is just about optimization. The current implementation already works well - and perhaps well enough for the majority of applications.

Also, it seems that further optimization (down to one DB request) would be restricted to a relatively small area of the code, and be mostly independent from the rest of the module. So it seems this optimization can be done at any later time (like when it's needed).

Restrictions & Caveats

The queryset methods values(), values_list(), select_related(), defer() and only() are not yet fully supported (see above)
Django 1.1 only - the names of polymorphic models must be unique in the whole project, even if they are in two different apps. This results from a restriction in the Django 1.1 "related_name" option (fixed in Django 1.2).
Django 1.1 only - when ContentType is used in models, Django's seralisation or fixtures cannot be used (all polymorphic models use ContentType). This issue seems to be resolved for Django 1.2 (changeset 11863: Fixed #7052, Added support for natural keys in serialization).
http://code.djangoproject.com/ticket/7052
http://stackoverflow.com/questions/853796/problems-with-contenttypes-when-loading-a-fixture-in-django
Diamond shaped inheritance: There seems to be a general problem with diamond shaped multiple model inheritance with Django models (tested with V1.1). An example is here: http://code.djangoproject.com/ticket/10808. This problem is aggravated when trying to enhance models.Model by subclassing it instead of modifying Django core (as we do here with PolymorphicModel).
Django Admin Integration: There currently is no admin integration, but it surely would be nice to have one. There is a discussion about it here: http://groups.google.de/group/django-polymorphic/browse_thread/thread/84290fe76c40c12d
It must be possible to instantiate the base model objects, even if your application never does this itself. This is needed by the current implementation of polymorphic querysets but (likely) also by Django internals. Example: If ModelB and ModelC inherit from ModelA, and you never create ModelA objects, django_polymorphic and Django core will still instantiate ModelA objects for temporary purposes (and fail, if this isn't possible).
A reference (ContentType) to the real/leaf model is stored in the base model (the base model directly inheriting from PolymorphicModel). If a model or an app is renamed, then Django's ContentType table needs to be corrected too, if the db content should stay usable after the rename.
For all objects that are not instances of the base class, but instances of a subclass, the base class fields are currently transferred twice from the database (an artefact of the current implementation's simplicity).
getattribute hack: For base model inheritance back relation fields (like basemodel_ptr), as well as implicit model inheritance forward relation fields, Django internally tries to use our polymorphic manager/queryset in some places, which of course it should not. Currently this is solved with a hacky getattribute in PolymorphicModel, which causes some overhead. A minor patch to Django core would probably get rid of that.
Project Status

It's important to consider that this code is very new and to some extent still experimental. It does seem to work very well for a number of people, but API changes, code reorganisations or further schema changes are still a possibility. There may also remain larger bugs and problems in the code that have not yet been found.

Links

http://code.djangoproject.com/wiki/ModelInheritance
http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html
http://www.djangosnippets.org/snippets/1031/
http://www.djangosnippets.org/snippets/1034/
http://groups.google.com/group/django-developers/browse_frm/thread/7d40ad373ebfa912/a20fabc661b7035d?lnk=gst&q=model+inheritance+CORBA#a20fabc661b7035d
http://groups.google.com/group/django-developers/browse_thread/thread/9bc2aaec0796f4e0/0b92971ffc0aa6f8?lnk=gst&q=inheritance#0b92971ffc0aa6f8
http://groups.google.com/group/django-developers/browse_thread/thread/3947c594100c4adb/d8c0af3dacad412d?lnk=gst&q=inheritance#d8c0af3dacad412d
http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/b76c9d8c89a5574f
http://peterbraden.co.uk/article/django-inheritance
http://www.hopelessgeek.com/2009/11/25/a-hack-for-multi-table-inheritance-in-django
http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the-name/929982#929982
http://stackoverflow.com/questions/1581024/django-inheritance-how-to-have-one-method-for-all-subclasses
http://groups.google.com/group/django-users/browse_thread/thread/cbdaf2273781ccab/e676a537d735d9ef?lnk=gst&q=polymorphic#e676a537d735d9ef
http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/bc18c18b2e83881e?lnk=gst&q=model+inheritance#bc18c18b2e83881e
http://code.djangoproject.com/ticket/10808
http://code.djangoproject.com/ticket/7270
django_polymorphic/doc (last edited 2010-02-01 15:16:21 by 82)

Cannot delete or update a parent row: a foreign key constraint fails

Using the delete_selected admin action can cause an error similar to the following:

IntegrityError: (1451, 'Cannot delete or update a parent row: a foreign key constraint fails (foo2.bar, CONSTRAINT format_ptr_id_refs_id_270cb9d612063f8e FOREIGN KEY (format_ptr_id) REFERENCES foo1 (id))')

Cascade event binding to inheriting models

Allow to bind events to all subclasses if binding to master model

when having polymorphic Models:

class ModelA(PolymorphicModel):
...

class ModelB(ModelA):
...

@receiver(post_save, sender=ModelA)
def do_something_after_save(...):

Right now I have to bind do_something_after_save to all subclasses of ModelA manually:

post_save.connect(do_something_after_save, sender=ModelA, weak=True)
post_save.connect(do_something_after_save, sender=ModelB, weak=True)

I have no idea if it is possible in djangoORM, or not, but coming django from sqlalchemy I took this behaviour for granted, while it isn't available right now.

Placing non polymorphic model after a polymorphic model can cause AttributeError no base_objects

Sometimes I'll get an error like this:

AttributeError type object XXX has no attribute 'base_objects'

The class XXXX is always one that inherits from models.Model, and generally later in my models file than a polymorphic class. Reordering the class file to put the normal models further up fixes it.

Here's an example trace, with 'Region' which extends model.Model causing the error:
https://gist.github.com/2966844

Model of Region:

class Region(models.Model):
    name = CharField(max_length=256)

    def __unicode__(self):
        return self.name

Charset error in polymorphic

The same error is given in plain shell (not ipython related problem)

(jpam)dev@dev:~$ python jpam/src/manage.py shell --plain
Python 2.7.3 (default, Apr 10 2013, 06:20:15) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from opps.multimedias.models import Video
>>> Video.objects.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/storage/dev/.virtualenvs/jpam/local/lib/python2.7/site-packages/polymorphic/query.py", line 292, in __repr__
    return super(PolymorphicQuerySet, self).__repr__(*args, **kwargs)
  File "/storage/dev/.virtualenvs/jpam/local/lib/python2.7/site-packages/django/db/models/query.py", line 80, in __repr__
    return repr(data)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe7' in position 3: ordinal not in range(128)

Unexpected 404 error

When in the admin delete view, if you try to get back to the change view using the last breadcrumb, a 404 error is raised by this line.

```python In [1]: Item.objects.all() Out[1]: --------------------------------------------------------------------------- DoesNotExist Traceback (most recent call last) <ipython-input-1-f68cadf55917> in <module>() ----> 1 Item.

In [1]: Item.objects.all()
Out[1]: ---------------------------------------------------------------------------
DoesNotExist                              Traceback (most recent call last)
<ipython-input-1-f68cadf55917> in <module>()
----> 1 Item.objects.all()

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/core/displayhook.pyc in __call__(self, result)
    236             self.start_displayhook()
    237             self.write_output_prompt()
--> 238             format_dict = self.compute_format_data(result)
    239             self.write_format_data(format_dict)
    240             self.update_user_ns(result)

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/core/displayhook.pyc in compute_format_data(self, result)
    148             MIME type representation of the object.
    149         """
--> 150         return self.shell.display_formatter.format(result)
    151 
    152     def write_format_data(self, format_dict):

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/core/formatters.pyc in format(self, obj, include, exclude)
    124                     continue
    125             try:
--> 126                 data = formatter(obj)
    127             except:
    128                 # FIXME: log the exception

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/core/formatters.pyc in __call__(self, obj)
    445                 type_pprinters=self.type_printers,
    446                 deferred_pprinters=self.deferred_printers)
--> 447             printer.pretty(obj)
    448             printer.flush()
    449             return stream.getvalue()

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/lib/pretty.pyc in pretty(self, obj)
    358                             if callable(meth):
    359                                 return meth(obj, self, cycle)
--> 360             return _default_pprint(obj, self, cycle)
    361         finally:
    362             self.end_group()

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/lib/pretty.pyc in _default_pprint(obj, p, cycle)
    478     if getattr(klass, '__repr__', None) not in _baseclass_reprs:
    479         # A user-provided repr.
--> 480         p.text(repr(obj))
    481         return
    482     p.begin_group(1, '<')

/home/vagrant/env/local/lib/python2.7/site-packages/polymorphic/query.pyc in __repr__(self, *args, **kwargs)
    290             return  '[ ' + ',\n  '.join(result) + ' ]'
    291         else:
--> 292             return super(PolymorphicQuerySet, self).__repr__(*args, **kwargs)
    293 
    294     class _p_list_class(list):

/home/vagrant/env/local/lib/python2.7/site-packages/django/db/models/query.pyc in __repr__(self)
     75 
     76     def __repr__(self):
---> 77         data = list(self[:REPR_OUTPUT_SIZE + 1])
     78         if len(data) > REPR_OUTPUT_SIZE:
     79             data[-1] = "...(remaining elements truncated)..."

/home/vagrant/env/local/lib/python2.7/site-packages/django/db/models/query.pyc in __len__(self)
     90                 self._result_cache = list(self.iterator())
     91         elif self._iter:
---> 92             self._result_cache.extend(self._iter)
     93         if self._prefetch_related_lookups and not self._prefetch_done:
     94             self._prefetch_related_objects()

/home/vagrant/env/local/lib/python2.7/site-packages/polymorphic/query.pyc in iterator(self)
    277                     break
    278 
--> 279             real_results = self._get_real_instances(base_result_objects)
    280 
    281             for o in real_results:

/home/vagrant/env/local/lib/python2.7/site-packages/polymorphic/query.pyc in _get_real_instances(self, base_result_objects)
    173 
    174                 else:
--> 175                     real_concrete_class = base_object.get_real_instance_class()
    176                     real_concrete_class_id = base_object.get_real_concrete_instance_class_id()
    177 

/home/vagrant/env/local/lib/python2.7/site-packages/polymorphic/polymorphic_model.pyc in get_real_instance_class(self)
    102         # when the content type record still exists but no longer refers to an existing model.
    103         try:
--> 104             return ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
    105         except AttributeError:
    106             # Django <1.6 workaround

/home/vagrant/env/local/lib/python2.7/site-packages/django/contrib/contenttypes/models.pyc in get_for_id(self, id)
    104             # This could raise a DoesNotExist; that's correct behavior and will
    105             # make sure that only correct ctypes get stored in the cache dict.
--> 106             ct = self.get(pk=id)
    107             self._add_to_cache(self.db, ct)
    108         return ct

/home/vagrant/env/local/lib/python2.7/site-packages/django/db/models/manager.pyc in get(self, *args, **kwargs)
    141 
    142     def get(self, *args, **kwargs):
--> 143         return self.get_query_set().get(*args, **kwargs)
    144 
    145     def get_or_create(self, **kwargs):

/home/vagrant/env/local/lib/python2.7/site-packages/django/db/models/query.pyc in get(self, *args, **kwargs)
    387                 "%s matching query does not exist. "
    388                 "Lookup parameters were %s" %
--> 389                 (self.model._meta.object_name, kwargs))
    390         raise self.model.MultipleObjectsReturned(
    391             "get() returned more than one %s -- it returned %s! "

DoesNotExist: ContentType matching query does not exist. Lookup parameters were {'pk': None}

Is table really needed when child model is a direct subclass?

So, I have a parent model and a child model. The child model has different methods and a different modelAdmin, but the model is the same:

class ParentModel(PolymorphicModel):
    column
    column
    column
    [...]

class ChildModel(ParentModel):
    pass

When I migrate ParentModel with south (it was a standard model before), a polymorphic_ctype is created in that table.

When I add ChildModel and migrate, south creates a new table:

def forwards(self, orm):
    # Adding model 'ChildModel'
    db.create_table(u'poly_childmodel', (
        (u'parentmodel_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['poly.parentmodel'], unique=True, primary_key=True)),
    ))
    db.send_create_signal(u'poly', ['ChildModel'])

Given that ChildModel will be completely contained in ParentModel's table, is the ChildModel really necessary?. Will joins be performed against it given that there is no extra data to query?. Is this just so a content type is created for Django?.

Thanks!

Uncaught DoesNotExist in _get_real_admin

If you use the admin integration, edit a polymorphic object, then change the id value in the URL so that no object can be found with this id. Instead of raising the regular Http404, the admin raises a DoesNotExist exception.

Filters & searches are lost after saving object

Hi!

Let's say I have a base class A and two child classes B and C. When I go into one of them, edit an object and press save, any searches and/or filters I had selected in the changeview list is lost. I get back everything.

The URL ends in ?e=1, which makes me believe that perhaps I should be authorizing or defining explicitly something in the ModelAdmin.

Unable to create polymorphic models from fixtures

Hi,

I've recently stumbled across django-polymorphic: firstly, thumbs up and thank-you! It makes dealing with subclasses a lot easier. It works almost perfectly except for one detail:

When I define an object in a fixture for a polymorphic model, I note it creates the subclass data but then forgets about the base class. e.g. I have a base class that looks like this:

class Driver(polymorphic.PolymorphicModel):
    name        = DriverNameField(primary_key=True,
            help_text=  'The driver name is a text string uniquely '\
                        'identifying this driver.  It should take the form '\
                        '\'${VENDOR_NAME}.${DRIVER_NAME}\', e.g. '\
                        '\'vrt.edmi\'.')

    description = models.CharField(max_length=80,help_text="""
        A human-readable description for this driver.""")

    data_formats = models.ManyToManyField(DataFormat,
            through='DriverDataFormat',
            through_fields=('driver', 'data_format'))
    documentation = models.TextField(blank=True,help_text="""A short
        description of the driver's function and capabilities""")

    def __unicode__(self):
        return u'%s' % (self.description)

    class Meta:
        ordering = ['description']

Notably, to avoid issues with clashing primary keys, I've used a string as a primary key. This model is meant to describe device drivers for various subsystems that get installed by other packages. One such subsystem is the MacroView SCADA server, for which I've defined a subclass:

class MVDriver(vrtweb_src.Driver):
    driver_type = models.CharField(max_length=16,
            help_text=  'Type of driver: specific to driver variant')

    executable = models.CharField('driver name',max_length='20',blank=True,
        help_text="""For drivers that require a separate user-space
        executable be launched to handle data processing, specify the
        executable here.""")
    offline_exe = models.CharField(max_length='20',blank=True,
        help_text="""Some drivers may require/support an "offline"
        mode where an alternate configuration can be run for testing
        or other purposes. If this driver needs to have another process
        running in place of the 'executable' field, specify it here.
        An example of this is for MacroView drivers where the actual
        driver can be substituted with a local source (e.g. localsrc,
        localimg, localimg64).""")
    offline_opt = models.CharField('Offline Options',
        max_length='80',blank=True)
    shm_used    = models.BooleanField('Uses Shared Memory', default=True)

    class Meta:
        app_label       = 'sources'
        verbose_name    = 'MacroView Driver'

If I go into the web UI and create a MVDriver object, it all works. Django-polymorphic does exactly what it says on the tin, and I get two objects: the Driver base-class, and the MVDriver specific class.

Perfect.

Then I try to load some via fixtures. I define some fixtures like so:

[
    {
        "model": "sources.mvdriver",
        "pk": "vrt.mv_simulator",
        "fields": {
            "description": "Built-in random simulator - basic",
            "driver_type": "1",
            "shm_used": false,
            "offline_exe": "",
            "offline_opt": "",
            "documentation": "The random simulated source is a very simple source that is designed to be used primarily to test displays and demonstrate systems. It essentially responds to the individual requests from the user by providing randomly varying numbers. It does however, treat some special attributes differently. For example, the Manipulated Variable, MV (or output), ranges from 0 to 100% (as we would expect). All other attributes are simply varied in a random fashion between scale high and scale low if they are analog and if they are digital, they are switched between the High and Low state in a random way."
        }
    },
    { 
        "model": "sources.mvdriver",
        "pk": "vrt.mv_dbase",
        "fields": {
            "description": "Built-in dBase table file driver", 
            "driver_type": "2",
            "shm_used": false,
            "offline_exe": "",
            "offline_opt": "",
            "documentation": "Using the dBase source, you may look at a dBase table file in exactly the same way as you would look at an entity say from a PLC. The dBase source makes dBase compatible files \u201clook like\u201d entities. This means that you can freely mix data from a real world source with data from a dBase file in a graphic or on a trend."
        }
    },
    {
        "model": "sources.mvdriver",
        "pk": "vrt.mv_text",
        "fields": {
            "description": "Built-in text file driver",
            "driver_type": "3",
            "shm_used": false,
            "offline_exe": "",
            "offline_opt": "",
            "documentation": "One of the most useful source drivers is the TEXT driver. This built-in reads data from an ASCII (flat) file and provides the data in the standard MacroView entity.attr format. Once it is in the ENTITY.ATTRIBUTE format, the data that originated in the text file can be used in exactly the same way as any other \u201creal world\u201d entity. For example, it can be used in graphics, group or detail displays, in high level programs etc. This source is particularly useful for reading in data that has been sent to the MacroView system from some another computer system.\r\n"
        }
    },
    {
        "model": "sources.mvdriver",
        "pk": "vrt.mv_localimg",
        "fields": {
            "description": "Local (in-memory) namespace driver - legacy",
            "driver_type": "5",
            "shm_used": true,
            "offline_exe": "localimg",
            "offline_opt": "",
            "documentation": "Very often, in process control systems, there is a need for entities that have no input or output. For example, a derived/calculated entity or a simulated entity. There is no need for this kind of data to be loaded into a PLC or DCS system. Local source driver entities reside within the MacroView system itself and behave in exactly the same manner as a real world sorted namespace driver. Unlike the local PLC driver, the local namespace driver does not require you to allocate and manage virtual PLC memory regions by hand. This is a legacy version of the local namespace driver that only supports 8 character entity and attribute names (17 character tags) and is becoming obsolete - use the newer version that supports 64 character entity and attribute names (129 character tags)."
        }
    },
    {
        "model": "sources.mvdriver",
        "pk": "vrt.mv_localsrc",
        "fields": {
            "description": "Local (in-memory) PLC driver",
            "driver_type": "6",
            "shm_used": true,
            "offline_exe": "localsrc",
            "offline_opt": "",
            "documentation": "Very often, in process control systems, there is a need for entities that have no input or output. For example, a derived/calculated entity or a simulated entity. There is no need for this kind of data to be loaded into a PLC or DCS system. Local PLC driver source is a source whose registers reside within the MacroView system itself and behave in exactly the same manner as a real world PLC system."
        }
    },
    {
        "model": "sources.mvdriver",
        "pk": "vrt.mv_sim_plc",
        "fields": {
            "description": "Built-in random simulator - PLC attributes",
            "driver_type": "7",
            "shm_used": false,
            "offline_exe": "",
            "offline_opt": "",
            "documentation": "The simulated PLC memory image driver is slightly different from the basic random simulator driver in that it uses the Type information structure to set up the random number generation. The simulation has access to the conversion data in the type attribute database. It therefore has an indication of such information as the range of the attribute. It can provide simulations of scaled analogs, integers, floating point values, digital status bits, etc."
        }
    },
    {
        "model": "sources.mvdriver",
        "pk": "vrt.mv_localimg64",
        "fields": {
            "description": "Local (in-memory) namespace driver",
            "driver_type": "9",
            "shm_used": true,
            "offline_exe": "localimg64",
            "offline_opt": "",
            "documentation": "Very often, in process control systems, there is a need for entities that have no external inputs. For example, a derived/calculated entity or a simulated entity. There is no need for this kind of data to be loaded into a PLC or DCS system. \r\nThe virtual Namespace driver source is a source whose entities reside within the MacroView system itself and behave in exactly the same manner as a real world sorted namespace driver. Unlike the virtual PLC driver, the virtual namespace driver does not require you to allocate and manage virtual PLC memory regions by hand."
        }
    }
]

and get this:

sqlite> select * from sources_mvdriver;
driver_ptr_id     driver_type  executable  offline_exe  offline_opt  shm_used  
----------------  -----------  ----------  -----------  -----------  ----------
vrt.mv_simulator  1                                                  0         
vrt.mv_dbase      2                                                  0         
vrt.mv_text       3                                                  0         
vrt.mv_localimg   5                        localimg                  1         
vrt.mv_localsrc   6                        localsrc                  1         
vrt.mv_sim_plc    7                                                  0         
vrt.mv_localimg6  9                        localimg64                1         
vrt.testdrv       6            testdrv     localimg64                1         
sqlite> select * from sources_driver;
polymorphic_ctype_id  name         description         documentation                   
--------------------  -----------  ------------------  --------------------------------
35                    vrt.testdrv  Driver to test ORM  This is a driver to test the ORM

The one lone row in sources_driver is the one I added by hand. Is there something special I need to do to correctly instantiate a polymorphic model from fixtures?

Possible idea for getting rid of the joins

I've used django-polymorphic a couple of times and it's great.

However - I'm trying to optimize the hell out of some code and got to thinking about that pesky INNER JOIN.

I think I've got an idea to do something similar to polymorphism but trading the cost of the join for the cost of some extra storage.

I dropped @chrisglass an email with the following and he suggested I posted it here so @vdboor and @Kronuz could chime in.

Tell me if you think there's anything worthwhile in the following:

  1. The superclass should be a concrete model that has the superset of all it's child model's fields.
  2. The children just have their own specific fields as normal
  3. You add an extra 'type' field to the superclass
  4. Each child sets the 'type' field on save to indicate which child that row relates to
  5. Each child has a custom manager so that the default queryset filters on the correct 'type'.

Voila. You can query the superclass or the children.

Converting one into the other is fairly easy as you always know the pk will the same.

Caveats

All fields that aren't common to all children must be nullable or have defaults.
I am not sure how reverse relationships to the superclass are going to work.

I can obviously do all this without any special code but some special magic could solve a few things.

  1. It could fix reverse relationships
  2. If could avoid duplicating the field definitions on the children (although I can already get round this by using abstract mixins)
  3. It could automatically handle the type field, setting type on save(), and the manager queryset.
  4. It could do some sanity checking.

I'd value your opinion on whether this is worth pursuing. Could it even be a 'mode' within django-polymorphic?

Prefetch_related has strange behavior

Hi,

I use django-polymorphic for a personal, non-critical project, in which items (Storable) can be stored in workspaces (Workspace).

This project relies heavily on concrete inheritance, and my models all inherit from a base model (using Polymorphic).

Everything works fine but, as always with concrete inheritance, database load is quite high, especially when I want to display a list of Storable, along with their workspaces.
In order to reduce the number of queries, I'm trying to use prefetch_related('workspaces') on Storable queryset. However, while it reduces indeed the number of queries, there is a bug when come the time to display items.

I've set up a test project with the following code.

Models

from django.db import models
from polymorphic import PolymorphicModel


class Base(PolymorphicModel):
    name = models.CharField('name', max_length=255, default=None, blank=True, null=True)


class Storable(Base):
    pass


class Workspace(Base):
    items = models.ManyToManyField(Storable, related_name="workspaces", null=True, blank=True, default = None)

View

from django.views.generic.list import ListView
from test_prefetch_related.models import Storable


class TestPrefetchRelated(ListView):
    model = Storable
    template_name = "test_prefetch_related/test_prefetch_related.html"
    paginate_by = 10

    def get_queryset(self):
        return self.model.objects.all().prefetch_related('workspaces')

Template

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    {% for item in object_list %}
        <h1>{{ item.name }}</h1>
        <ul>
            {% for w in item.workspaces.all %}
                <li>{{ w.id }} - {{ w.name }}</li>
            {% empty %}
                None
            {% endfor %}
        </ul>
    {% endfor %}
</body>
</html>

Database population

from test_prefetch_related.models import Storable, Workspace

# here is the code I used to populate the database with some data

# create workspaces
w1 = Workspace(name="w1")
w1.save()

w2 = Workspace(name="w2")
w2.save()

w3 = Workspace(name="w3")
w3.save()

# create storables
s1 = Storable(name="s1")
s1.save()
s1.workspaces.add(w1)
s1.save()

s2 = Storable(name="s2")
s2.save()
s2.workspaces.add(w2)
s2.save()

s3 = Storable(name="s3")
s3.save()
s3.workspaces.add(w1, w2, w3)  # this storable get all workpaces
s3.save()

Html output

s1

    1 - w1
    1 - w1

s2

    2 - w2
    2 - w2

s3

    3 - w3

The bug

It's hard to explain (excuse my english ;), but as you can see in output, workspaces are not displayed properly, next to each Storable. Intead, they are grouped on the first item in the queryset with which they are linked.

The expected output would be :

s1

    1 - w1

s2

    2 - w2

s3     

    1 - w1
    2 - w2
    3 - w3

I'm almost sure it is related to polymorphic because the following code using standard django inheritance works as expected.

In polymorphic docs, I did not found any mention of prefetch_related(). Is this behaviour one of the caveats of using concrete inheritance ? Is there anything else I could do to achieve the same result ?

You can download the code I used, if you want to check by yourself : http://seafile.eliotberriot.com/d/aca8f2399a/ (click on ZIP button in top right corner).

Thank you for your help and for this project, apart from that, I'm really happy with it !

Docs

Sorry for the noise.

copy polymorphic model instance

I try to copy a polymorphic model instance, with normal objects I'd di

ob.pk = None
ob.save()

but this doesn't work for a polymorphic instance, I end up with some kind of unbound entry. Is there any documentation on how to do this? Or any pointers?

Impossible to use base_fieldsets (max recursion depth exceeded)

When using the admin integration like in the docs, the following error is raised when base_fieldsets is defined: RuntimeError: maximum recursion depth exceeded while calling a Python object.

The reason is because PolymorphicChildModelAdmin.get_form calls .get_fieldsets which calls .get_subclass_fields which callsโ€ฆ .get_form.

Since I don't understand perfectly how this should work, I'm unable to make a pull request for this.

Django Admin StackedInline

I'm a newbie to Django.
So I'm not sure if this is an issue with django-polymorphic or I'm doing something stupid (it's probably the latter! )

I have a PolymorphicChildModel (Snippet) as StackedInline for a standard django model (Page).

models.py


class Page(models.Model):
... page model

class Widget(PolymorphicModel):
page = models.ForeignKey(Page)
....

class Snippet(Widget):
html = models.TextField()
....

admin.py

class SnippetInline(admin.StackedInline):
model = Snippet
....

class PageAdmin(admin.ModelAdmin):
inlines = [SnippetInline. ...]
...

The StackedInline are displayed correctly. However when I try to save it..

error

I would really appreciate any help. Thanks in advance.

Two level of inheritance

Does Django_polymorphic support two level of inheritance, it doesn't seem to be working on [https://github.com/amirouche/django-wall] I get the following exception:

AttributeError at /admin/wall/embed/add/

can't set attribute

Request Method:     GET
Request URL:    http://127.0.0.1:8000/admin/wall/embed/add/
Django Version:     1.4.1
Exception Type:     AttributeError
Exception Value:    

can't set attribute

Exception Location:     /home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/db/models/base.py in __init__, line 357
Python Executable:  /home/amirouche/.virtualenvs/django/bin/python
Python Version:     2.7.3

Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/admin/wall/embed/add/

Django Version: 1.4.1
Python Version: 2.7.3
Installed Applications:
('django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.admin',
 'wall')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware')


Traceback:
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  111.                         response = callback(request, *callback_args, **callback_kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
  366.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  91.                     response = view_func(request, *args, **kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  89.         response = view_func(request, *args, **kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/contrib/admin/sites.py" in inner
  196.             return view(request, *args, **kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  25.             return bound_func(*args, **kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  91.                     response = view_func(request, *args, **kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
  21.                 return func(self, *args2, **kwargs2)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/db/transaction.py" in inner
  209.                 return func(*args, **kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/contrib/admin/options.py" in add_view
  970.             form = ModelForm(initial=initial)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/forms/models.py" in __init__
  234.             self.instance = opts.model()
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/polymorphic/polymorphic_model.py" in __init__
  135.         super(PolymorphicModel, self).__init__(*args, ** kwargs)
File "/home/amirouche/.virtualenvs/django/local/lib/python2.7/site-packages/django/db/models/base.py" in __init__
  357.                 setattr(self, field.attname, val)

Exception Type: AttributeError at /admin/wall/embed/add/
Exception Value: can't set attribute

Abstract base classes lose the custom objects manager [Django 1.5+]

Possible duplicate of #64

Despite what the docs say it is possible to define a custom objects manager on polymorphic models, as long as you inherit django polymorphic the right way. However since Django 1.5 the objects manager is overwritten by django polymorphic, causing unwanted and unexpected behaviour.

The following scenario has worked before the upgrade to django 1.5 (from 1.4.x):

class TenantManager(models.Manager):
    use_for_related_fields = True

    def get_query_set(self):
        """
        Manipulate the returned queryset by adding a filter for tenant using the tenant linked
        to the current logged in user (received via custom middleware).
        """
        user = get_current_user()
        if user and user.is_authenticated():
            return super(TenantManager, self).get_query_set().filter(tenant=user.tenant)
        else:
            return super(TenantManager, self).get_query_set()


class PolymorphicTenantManager(TenantManager, PolymorphicManager):
    pass


class MultiTenantMixin(models.Model):
    # Automatically filter any queryset by tenant if logged in
    objects = TenantManager()

    # blank=True to allow form validation (tenant is set upon model.save)
    tenant = models.ForeignKey(Tenant, blank=True)

    def save(self, *args, **kwargs):
        user = get_current_user()
        if user and user.is_authenticated():
            self.tenant = user.tenant

        return super(MultiTenantMixin, self).save(*args, **kwargs)

    class Meta:
        abstract = True


class PolymorphicMultiTenantMixin(PolymorphicModel, MultiTenantMixin):
    # Automatically filter any queryset by tenant if logged in and downcast to lowest possible class
    objects = PolymorphicTenantManager()

    class Meta:
        abstract = True


class HistoryListItem(PolymorphicTenantMixin):
    """
    An base model for all items that can appear in a History List
    """
    fields...

We have this code to automatically filter by tenant in all generic views and querys. The way it is set up like this is because we also support environments without tenants (which would drop the filter), but that isn't shown here. After some debugging I came across the following code which seems to break it (located in base.py) :

if AbstractManagerDescriptor is not None:
    # Django 1.4 unconditionally assigned managers to a model. As of Django 1.5 however,
    # the abstract models don't get any managers, only a AbstractManagerDescriptor as substitute.
    # Pretend that the manager is still there, so all code works like it used to.
    if type(manager) == AbstractManagerDescriptor and base.__name__ == 'PolymorphicModel':
        model = manager.model
        if key == 'objects':
            manager = PolymorphicManager()
            manager.model = model
        elif key == 'base_objects':
            manager = models.Manager()
            manager.model = model

So despite the comment at L128 it actually alters behaviour by overwriting my custom objects manager. I have not found a proper fix, but for me commenting those lines works. However, if I understand correctly, that would break abstract classes that don't set the objects manager specifically?

DoesNotExist: ContentType matching query does not exist. Lookup parameters were {'pk': None}

Django 1.5.1
After south migration.

In [1]: Item.objects.all()
Out[1]: ---------------------------------------------------------------------------
DoesNotExist                              Traceback (most recent call last)
<ipython-input-1-f68cadf55917> in <module>()
----> 1 Item.objects.all()

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/core/displayhook.pyc in __call__(self, result)
    236             self.start_displayhook()
    237             self.write_output_prompt()
--> 238             format_dict = self.compute_format_data(result)
    239             self.write_format_data(format_dict)
    240             self.update_user_ns(result)

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/core/displayhook.pyc in compute_format_data(self, result)
    148             MIME type representation of the object.
    149         """
--> 150         return self.shell.display_formatter.format(result)
    151 
    152     def write_format_data(self, format_dict):

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/core/formatters.pyc in format(self, obj, include, exclude)
    124                     continue
    125             try:
--> 126                 data = formatter(obj)
    127             except:
    128                 # FIXME: log the exception

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/core/formatters.pyc in __call__(self, obj)
    445                 type_pprinters=self.type_printers,
    446                 deferred_pprinters=self.deferred_printers)
--> 447             printer.pretty(obj)
    448             printer.flush()
    449             return stream.getvalue()

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/lib/pretty.pyc in pretty(self, obj)
    358                             if callable(meth):
    359                                 return meth(obj, self, cycle)
--> 360             return _default_pprint(obj, self, cycle)
    361         finally:
    362             self.end_group()

/home/vagrant/env/local/lib/python2.7/site-packages/IPython/lib/pretty.pyc in _default_pprint(obj, p, cycle)
    478     if getattr(klass, '__repr__', None) not in _baseclass_reprs:
    479         # A user-provided repr.
--> 480         p.text(repr(obj))
    481         return
    482     p.begin_group(1, '<')

/home/vagrant/env/local/lib/python2.7/site-packages/polymorphic/query.pyc in __repr__(self, *args, **kwargs)
    290             return  '[ ' + ',\n  '.join(result) + ' ]'
    291         else:
--> 292             return super(PolymorphicQuerySet, self).__repr__(*args, **kwargs)
    293 
    294     class _p_list_class(list):

/home/vagrant/env/local/lib/python2.7/site-packages/django/db/models/query.pyc in __repr__(self)
     75 
     76     def __repr__(self):
---> 77         data = list(self[:REPR_OUTPUT_SIZE + 1])
     78         if len(data) > REPR_OUTPUT_SIZE:
     79             data[-1] = "...(remaining elements truncated)..."

/home/vagrant/env/local/lib/python2.7/site-packages/django/db/models/query.pyc in __len__(self)
     90                 self._result_cache = list(self.iterator())
     91         elif self._iter:
---> 92             self._result_cache.extend(self._iter)
     93         if self._prefetch_related_lookups and not self._prefetch_done:
     94             self._prefetch_related_objects()

/home/vagrant/env/local/lib/python2.7/site-packages/polymorphic/query.pyc in iterator(self)
    277                     break
    278 
--> 279             real_results = self._get_real_instances(base_result_objects)
    280 
    281             for o in real_results:

/home/vagrant/env/local/lib/python2.7/site-packages/polymorphic/query.pyc in _get_real_instances(self, base_result_objects)
    173 
    174                 else:
--> 175                     real_concrete_class = base_object.get_real_instance_class()
    176                     real_concrete_class_id = base_object.get_real_concrete_instance_class_id()
    177 

/home/vagrant/env/local/lib/python2.7/site-packages/polymorphic/polymorphic_model.pyc in get_real_instance_class(self)
    102         # when the content type record still exists but no longer refers to an existing model.
    103         try:
--> 104             return ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
    105         except AttributeError:
    106             # Django <1.6 workaround

/home/vagrant/env/local/lib/python2.7/site-packages/django/contrib/contenttypes/models.pyc in get_for_id(self, id)
    104             # This could raise a DoesNotExist; that's correct behavior and will
    105             # make sure that only correct ctypes get stored in the cache dict.
--> 106             ct = self.get(pk=id)
    107             self._add_to_cache(self.db, ct)
    108         return ct

/home/vagrant/env/local/lib/python2.7/site-packages/django/db/models/manager.pyc in get(self, *args, **kwargs)
    141 
    142     def get(self, *args, **kwargs):
--> 143         return self.get_query_set().get(*args, **kwargs)
    144 
    145     def get_or_create(self, **kwargs):

/home/vagrant/env/local/lib/python2.7/site-packages/django/db/models/query.pyc in get(self, *args, **kwargs)
    387                 "%s matching query does not exist. "
    388                 "Lookup parameters were %s" %
--> 389                 (self.model._meta.object_name, kwargs))
    390         raise self.model.MultipleObjectsReturned(
    391             "get() returned more than one %s -- it returned %s! "

DoesNotExist: ContentType matching query does not exist. Lookup parameters were {'pk': None}

Tests Fail when run in the normal test

When running tests on polymorphic when in my own project I'm getting these failed tests. I'm using postgres 9.2 and django 1.5.1 w/ py2.7 in 12.

 ./manage.py test polymorphic
Creating test database for alias 'default'...
.....
# known django model inheritance diamond problem detected
DiamondXY fields 1: field_b "", field_x "x", field_y "y"
DiamondXY fields 2: field_b "", field_x "x", field_y "y"
.E.FFF..F..FFF
# known type inconstency with custom primary key field detected (django problem?)
....FFFF.
======================================================================
ERROR: test_extra_method (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 523, in test_extra_method
    self.assertEqual(repr(objects[0]), '<Model2B: id 2, field1 (CharField), field2 (CharField)>')
IndexError: list index out of range

======================================================================
FAIL: test_foreignkey_field (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 471, in test_foreignkey_field
    self.assertEqual(repr(object2a.model2b), '<Model2B: id 3, field1 (CharField), field2 (CharField)>')
AssertionError: '<Model2B: id 11, field1 (CharField), field2 (CharField)>' != '<Model2B: id 3, field1 (CharField), field2 (CharField)>'

======================================================================
FAIL: test_get_real_instances (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 426, in test_get_real_instances
    self.assertEqual(repr(objects[0]), '<Model2A: id 1, field1 (CharField)>')
AssertionError: '<Model2A: id 13, field1 (CharField)>' != '<Model2A: id 1, field1 (CharField)>'

======================================================================
FAIL: test_instance_of_filter (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 553, in test_instance_of_filter
    self.assertEqual(repr(objects[0]), '<Model2B: id 2, field1 (CharField), field2 (CharField)>')
AssertionError: '<Model2B: id 18, field1 (CharField), field2 (CharField)>' != '<Model2B: id 2, field1 (CharField), field2 (CharField)>'

======================================================================
FAIL: test_manual_get_real_instance (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 407, in test_manual_get_real_instance
    self.assertEqual(repr(o.get_real_instance()), '<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>')
AssertionError: '<Model2C: id 23, field1 (CharField), field2 (CharField), field3 (CharField)>' != '<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>'

======================================================================
FAIL: test_non_polymorphic (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 414, in test_non_polymorphic
    self.assertEqual(repr(objects[0]), '<Model2A: id 1, field1 (CharField)>')
AssertionError: '<Model2A: id 25, field1 (CharField)>' != '<Model2A: id 1, field1 (CharField)>'

======================================================================
FAIL: test_onetoone_field (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 484, in test_onetoone_field
    self.assertEqual(repr(b.one2one), '<Model2A: id 3, field1 (CharField)>')
AssertionError: '<Model2A: id 31, field1 (CharField)>' != '<Model2A: id 3, field1 (CharField)>'

======================================================================
FAIL: test_polymorphic___filter (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 580, in test_polymorphic___filter
    self.assertEqual(repr(objects[0]), '<Model2B: id 2, field1 (CharField), field2 (CharField)>')
AssertionError: '<Model2B: id 34, field1 (CharField), field2 (CharField)>' != '<Model2B: id 2, field1 (CharField), field2 (CharField)>'

======================================================================
FAIL: test_relation_base (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 641, in test_relation_base
    self.assertEqual(repr(objects[0]), '<RelationB: id 3, field_base (CharField) "B1", fk (ForeignKey) RelationA, field_b (CharField) "B2", m2m (ManyToManyField) 1>')
AssertionError: '<RelationBC: id 4, field_base (CharField) "C1", fk (ForeignKey) RelationA, field_b (CharField) "C2", field_c (CharField) "C3", m2m (ManyToManyField) 0>' != '<RelationB: id 3, field_base (CharField) "B1", fk (ForeignKey) RelationA, field_b (CharField) "B2", m2m (ManyToManyField) 1>'

======================================================================
FAIL: test_simple_inheritance (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 397, in test_simple_inheritance
    self.assertEqual(repr(objects[0]), '<Model2A: id 1, field1 (CharField)>')
AssertionError: '<Model2A: id 37, field1 (CharField)>' != '<Model2A: id 1, field1 (CharField)>'

======================================================================
FAIL: test_translate_polymorphic_q_object (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 444, in test_translate_polymorphic_q_object
    self.assertEqual(repr(objects[0]), '<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>')
AssertionError: '<Model2C: id 43, field1 (CharField), field2 (CharField), field3 (CharField)>' != '<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>'

======================================================================
FAIL: test_user_defined_manager (polymorphic.tests.PolymorphicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/mike/code/django_polymorphic/polymorphic/tests.py", line 661, in test_user_defined_manager
    self.assertEqual(repr(objects[0]), '<ModelWithMyManager: id 6, field1 (CharField) "D1b", field4 (CharField) "D4b">')
AssertionError: '<ModelWithMyManager: id 50, field1 (CharField) "D1b", field4 (CharField) "D4b">' != '<ModelWithMyManager: id 6, field1 (CharField) "D1b", field4 (CharField) "D4b">'

----------------------------------------------------------------------
Ran 28 tests in 0.554s

FAILED (failures=11, errors=1)
Destroying test database for alias 'default'...

UUID field and admin error

My models use UUID as primary-key. I used cramer/django-uuidfield, because I got a problem using south, if I use UUID field defined in project test_tools.

If I use django_polymorphic and cramer/django-uuidfield together, I get this error, when I try to show a models list in admin interface:

Reverse for 'base_devices_basedevice_change' with arguments 
'(UUID('9639a9909ed24a99ae20f2c0f5c631ba'),)' and keyword arguments '{}' not found. 
1 pattern(s) tried: [u'admin/base_devices/basedevice/(\\d+)/$']

I solved it changing in PolymorphicParentModelAdmin class, line 250:

new_change_url = url(r'^(\d+)/$', self.admin_site.admin_view(self.change_view), name='{0}_{1}_change'.format(*info))

to

new_change_url = url(r'^(\w+)/$', self.admin_site.admin_view(self.change_view), name='{0}_{1}_change'.format(*info))

Have somebody a better solution??

Django Foreignkey to parent Model

Hello,
Thanks for your awesome modules,
I don't know how to explain this,
i'm a real newbie to django.
I have this models :

class Product(models.Model):
    name = models.CharField(max_length=255)

class Media(PolymorphicModel):
    name = models.CharField(max_length=255)
    product = models.ForeignKey(Product)

class Picture(Media):
    picture = FilerImageField(null=True, blank=True)

class Video(Media)
    video_type = models.CharField( max_length=50)

How can i set the manytoone in admin for the product model?
I just tried :

class MediaInline(admin.TabularInline):
    model = Media
    extra = 3

class ProductAdmin(reversion.VersionAdmin):
    fieldsets = [
        ('General',          {'fields': ['name', 'title', 'reference']}),
        ('Price information', {'fields': ['price']}),
    ]
    inlines = [MediaInline]

admin.site.register(Product, ProductAdmin)

But i don't have the extra media fields :/
How can i do that?

Django recent Actions link returns 404

When you modify a DjangoPolymorphic model in the Django-admin an entry is added in the Recent-Actions list.

However, when you click on the action it returns a 404.
This is because the url uses the derrived slug of the real instance instead of the slug of the BaseTreeNode.

Now (this returns 404):
/admin/app/edition/29/

This works:
/admin/app/basetreenode/29/

With models.py:

class BaseTreeNode(PolymorphicMPTTModel):
    pass


class Edition(BaseTreeNode):
    content = fields.HTMLField(_('content'))

Suggested solutions:

Make sure /admin/app/edition/29/ returns 200
Or make sure the entry in django-admin uses 'basetreenode' instead of 'edition'

Problem with custom pk (id) field - IntegrityError: NOT NULL constraint failed: *_ptr_id

I get an error when saving the model with custom pk field.

My model:

class ModelZ(models.Model):
    id = models.CharField(primary_key=True, max_length=7)
    field0 = models.CharField(max_length=10)
    class Meta:
        abstract = True

class ModelA(PolymorphicModel, ModelZ):
    field1 = models.CharField(max_length=10)

class ModelB(ModelA):
    field2 = models.CharField(max_length=10)

Error:

ModelB.objects.create(pk='01', field0='09')
[...]
IntegrityError: NOT NULL constraint failed: myapp_modelb.modela_ptr_id

Without custom pk field in ModelZ I can save object.
From admin page I can add an objects, there are no errors.

Django==1.6.5
django-polymorphic==0.5.6

Reverse related object descriptor is overwritten on class

This line overwrites the parent model class reverse related field object descriptor with a property function (at model instantiation), which breaks references to the field and its attributes for other interfaces. See this issue which arose in the implementation of a model relation introspection app on a model hierarchy using django-polymorphic

django-reversion support

Is there a way to properly use django-reversion with polymorphic admin?
I tried, but this leads to a TypeError:

Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases MyAdmin, VersionAdmin, PolymorphicChildModelAdmin

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.