Git Product home page Git Product logo

django-admin-flexselect's Introduction

About Django Admin FlexSelect

FlexSelect is a small app for the Django Admin that makes it trivial to have foreign keys depend on each other. By depend I mean that choices and additional content of one field updates dynamically when another is changed.

Usage example

See the video at http://www.youtube.com/watch?v=ooii3iCTZ6o.

In the following we will define a Case model with two foreign key fields, a base field client and a trigger field company_contact_person. When we change the client on the Case change view the company_contact_person updates accordingly. Furthermore we will display the customer_contact_persons company and email as additional details.

In "models.py":

from django.db import models as m
from django.core.exceptions import ValidationError

"""
No changes to the models are needed to use flexselect.
"""

class Company(m.Model):
    name = m.CharField(max_length=80)
    
    def __unicode__(self):
        return self.name

class CompanyContactPerson(m.Model):
    company = m.ForeignKey(Company)
    name = m.CharField(max_length=80)
    email = m.EmailField()
    
    def __unicode__(self):
        return self.name
    
class Client(m.Model):
    company = m.ForeignKey(Company)
    name = m.CharField(max_length=80)
    
    def __unicode__(self):
        return self.name
     
class Case(m.Model):
    client = m.ForeignKey(Client)
    company_contact_person = m.ForeignKey(CompanyContactPerson)
    
    def clean(self):
        """
        Makes sure the company for client is the same as the company for the 
        customer contact person. Note that you need to check for `None` too
        if the fields are not required.
        """
        if not self.client.company == self.company_contact_person.company:
            raise ValidationError('The clients and the contacts company does'
                                  ' not match.')
    
    def __unicode__(self):
        return u'Case: %d' % self.id

In "admin.py":

from django.contrib import admin
from flexselect import FlexSelectWidget
from test_flexselect.tests.models import (Company, Case, Client, 
                                          CompanyContactPerson,)

class CompanyContactPersonWidget(FlexSelectWidget):
    """
    The widget must extend FlexSelectWidget and implement trigger_fields, 
    details(), queryset() and empty_choices_text().
    """
    
    trigger_fields = ['client']
    """Fields which on change will update the base field."""
    
    def details(self, base_field_instance, instance):
        """
        HTML appended to the base_field.
        
        - base_field_instance: An instance of the base_field.
        - instance: A partial instance of the parent model loaded from the
                    request.
                    
        Returns a unicoded string.
        """
        return u"""\
        <div>
            <dl>
                <dt>%s</dt><dd>%s</dd>
                <dt>%s</dt><dd>%s</dd>
            </dl>
        </div>
        """ % ('Company', base_field_instance.company,
               'Email',  base_field_instance.email,
              )
        
    def queryset(self, instance):
        """
        Returns the QuerySet populating the base field. If either of the
        trigger_fields is None, this function will not be called.
        
        - instance: A partial instance of the parent model loaded from the
                    request.
        """
        company = instance.client.company
        return CompanyContactPerson.objects.filter(company=company)
    
    def empty_choices_text(self, instance):
        """
        If either of the trigger_fields is None this function will be called
        to get the text for the empty choice in the select box of the base
        field.
        
        - instance: A partial instance of the parent model loaded from the
                    request.
        """
        return "Please update the client field"
    
class CaseAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        """
        Alters the widget displayed for the base field.
        """
        if db_field.name == "company_contact_person":
            kwargs['widget'] =  CompanyContactPersonWidget(
                base_field=db_field,
                modeladmin=self,
                request=request,
            )
            kwargs['label'] = 'Contact'
        return super(CaseAdmin, self).formfield_for_foreignkey(db_field, 
            request, **kwargs)

class ClientAdmin(admin.ModelAdmin):
    pass

class CompanyContactPersonAdmin(admin.ModelAdmin):
    pass

class CompanyAdmin(admin.ModelAdmin):
    pass

admin.site.register(Case, CaseAdmin)
admin.site.register(Client, ClientAdmin)
admin.site.register(CompanyContactPerson, CompanyContactPersonAdmin)
admin.site.register(Company, CompanyAdmin)

Installation

sudo pip install django-admin-flexselect

Configuration

  1. Add "flexselect", to INSTALLED_APPS in "settings.py".

  2. Add url(r'^flexselect/', include('flexselect.urls')), to "urls.py".

Options

As of yet, flexselect only have one configuration option, namely "include_jquery" that if set to true will include jQuery and jQueryUI from the google ajax CDN. It defaults to false and the entire FLEXSELECT dict can be omitted.

# Flexselect settings.
FLEXSELECT = {
    'include_jquery': True,
}

Static files

FlexSelect requires "django.contrib.staticfiles" installed to work out of the box. If it is not then include "jQuery", "jQueryUI" and "flexselect/static/flexselect/js/flexselect.js" manually. Read more about "django.contrib.staticfiles" at https://docs.djangoproject.com/en/1.3/ref/contrib/staticfiles/.

django-admin-flexselect's People

Contributors

ropable avatar runekaagaard avatar winzard 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

Watchers

 avatar  avatar  avatar  avatar  avatar

django-admin-flexselect's Issues

Does not work with Django 1.7

I am using this app in Django 1.7.8

I am getting the error: No module named defaults

Seems like the defaults module was removed in Django 1.6

Thread safety

Hello,

This package does not currently appear to be thread safe. When running this package under gunicorn with multiple workers (I used 4 for my sample), flex select fails with the below traceback. For your reference my setup was django 1.5.5 on Python 2.7.3 with nginx proxying to gunicorn 18.0.

2013-11-18 08:57:51,967 django.request ERROR Internal Server Error: /flexselect/field_changed
Traceback (most recent call last):
  File "/home/xxx/xxx/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 115, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/home/xxx/xxx/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py", line 25, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/home/xxx/xxx/local/lib/python2.7/site-packages/flexselect/views.py", line 17, in field_changed
    widget = FlexSelectWidget.instances[hashed_name]
KeyError: u'_7f7204089cbe1187f19ebc6d47c87c273bb9bb52'

I think this is due to the way that the FlexWidgets are being stored in the module/memory. I am currently working to find a fix to the issue. I will raise a pull request when I have something that works.

Many thanks,
Rob

Double chain doesn't propagate change

I chained a fk select to another and then a third one to the second. When the "root" select is changed, the second select is adjusted accordingly but the third one isn't reset.

Doesn't work on Django 1.9?

This doesn't work on Django 1.9 - is there another way to do this now?

error: ImportError: cannot import name execute_manager

Prepopulate when there's already a default?

Hi,
thanks for flexselect! It's definitely the cleanest solution to django admin chained selects around.

If I give the triggering field an initial value then I currently have to do a Javascript call to change() manually.
Would be great if the trigger fields were checked for an existing value and triggered initially if there was any.

Cheers,
Danny

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.