Skip to content

J.A.C.K.F.R.O.S.T. (Jquery Auto Complete Kit For Remote Object Safe Targeting) is an autocomplete technology based on the jquery ui plugin which allows the search and pick of remote objects (i.e. stored model objects) from the front end as a field value, supporting any dataset/query and having that object at the server side.

Notifications You must be signed in to change notification settings

luismasuelli/jackfrost

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jackfrost

J.A.C.K.F.R.O.S.T. (Jquery Auto Complete Kit For Remote Object Safe Targeting) is an autocomplete technology based on the jquery ui plugin which allows the search and pick of remote objects (i.e. stored model objects) from the front end as a field value, supporting any dataset/query and having that object at the server side.

Installing

Put the application package named jackfrost in your project. The application package is named jackfrost and is inside as the project directory, but located in the same level of this file (i.e. installed as an app before jackfrost_app and test in this project).
It is safe to copy the application package as is to your project.

In settings.py:

  • Install the application.
  • Customize the jQuery library (static files) in these variables (Note: the application ships with a version of jquery, jqueryui, and a theme. If one or more of these settings are not specified, their shipped defaults will be used instead):
    • JACKFROST_JQUERY_LIB : path to the jquery library.
    • JACKFROST_JQUERYUI_LIB : path to the jquery-ui library.
    • JACKFROST_JQUERYUI_CSS : path to the theme to use by the application.

Using

This application was meant to provide alternatives to the ModelChoiceField and ModelMultipleChoiceField components by allowing real-time AJAX searches from the client side. Three form fields are provided and meant to use for simple foreign keys or many-to-many relationships.

In the model declarations:

  • Declare your models as usual.

In the views declarations you will declare "channels" or "lookups". This is the most important thing since these channels will be used for every ajax request performed by the component. Channels are not themselves views/callables, but objects which contain views for the needed ajax calls, so you must declare yourchannels and then import them for their urls.

  1. Declaring a channel: In module jackfrost.lookups a function named "register" is used to create them.

    • Parameters (ordered by position, with the exact name):
      • pattern: The url pattern you would use to declare it as a normal view. This pattern must contain a single "%s" format marker because it will be used to create three urls: each one replacing the %s for "many", "one", and "ac" (respectively: M2M initialization, FK initialization, and autocomplete search). e.g. r'/foo/bar/%s/' or r'/foo/mychannel-%s/'
      • name: The name to perform an url reversal. for each generated url, a name will be generated from this name by concatenating "-many", "-one" and "ac" respectively.
        The name is both used to register the channel object and to obtain it back.
      • queryset: A query to search the items from, and validate the items against. The query MUST be the same as (or a subset of) the source for the ForeignKey or ManyToMany for which it's intended. IMPORTANT: for that model class, __unicode__(self) must be defined since it will be the caption for the autocomplete results, selection, and initialization.
      • filter (optional): None or a callable as follows:
        • Expected parameters (by position):
          • query: The query specified in queryset parameter.
          • request: The current WSGIRequest object (it will be an AJAX request).
        • Expected return value: A queryset obtained from the query parameter, e.g., by taking into account current request details like user, site, or session and applying filters to it.
        • Remarks: Specifying None instead of a callable does not perform any filtering (the queryset is not modified/filtered).
      • field_list: A sequence of fields in the query to perform the search against.
        For every search term, a LIKE comparison is performed against each field in this list. e.g. passing ('name', 'description') will list every item having at least one search term in any part of those fields contents. THOSE FIELDS MUST BE VARCHAR OR TEXT FIELDS.
      • limit (optional): A positive integer specifying the max amount of results to retrieve for the autocomplete jQuery plugin. Defaults to 15.
      • to_field_name (optional): The field, in the target model class, to validate against when objects are validated or initialized.
        It should be a primary or unique key. Otherwise there's a big chance that validation errors occur or keys be initialized to an unintended model instance. It defaults to "pk" (the alias for every model to the model-specific pk field).
      • throw403_if (optional): None or a callable as follows:
        • Expected parameters (by position):
          • request: The current (AJAX) request.
          • context: "ac" (if it's the channel's autocomplete url) "one" (for the ForeignKey initialization url) or "many" (for the ManyToMany initialization url)
        • Expected return value: A value evaluable to True will cause the current AJAX call to fail with a 403 (forbidden) HTTP error. a value evaluable to False will not.
        • Remarks: specifying None will not perform this check-and-error.
      • throw404_if (optional): None or a callable like throw403_if except that the raised error response will be 404 (Not Found) instead of 403 (Forbidden).
      • extra_data_getter (optional): None or a callable as follows:
        • Expected parameters (by position):
          • instance: An instance fetched by the queryset or filtered queryset.
        • Expected return value: A JSON-serializable value representing some extra data needed in the client side (perhaps used by a custom render in the jQuery ui component).
        • Remarks: Specifying None will cause the extra data to be a void object (i.e. an empty dict in python and an empty object in javascript).
  2. After registering it, it must be referenced by it's urls.
    In module jackfrost.lookups a variable named registered_lookups will reference a dictionary containing the current lookup by it's name (2nd parameter specified in the lookup register() call). It's url pack must be got. e.g. an urls.py file (usually, you will put these crap in those urls.py files):

    #put this import among the other needed imports
    from jackfrost import lookups

    #... more stuff here you would need

    #sample call with no optional parameters
    lookups.register(r'/channels/first-%s/', 'myFirstChannel', MyModel.objects.all())

    #... more stuff here you would need

    url_patterns = patterns('',) + lookups.get_lookup_urls('myFirstChannel')
    #you can add in a single call as many channels as you want (this example adds only one channel)
    #specifying them as more positional arguments.

    #NOTE: watch out your calls to get_lookup_urls. Remember that each parameter is a name for an url. #So don't include a name which was included in another previous call to get_lookup_urls.

    PLEASE TAKE NOTE TO AVOID FUTURE HEADACHES: This application doesn't work with namespaces!!
    There's nothing as badly-designed as namespacing in django is. There are numerous issues with that regarding reusable applications.
    This is because of two contradictory points in that feature's design:

    1. It's (most of the time) the application designer the one who creates the application templates and calls {% url %} and reverse() for application-specific urls. So both calls force the designer to specify, at least, a fixed app_name.
    2. It's the application importer (a.k.a. user) the one who imports the urls with a namespace. So it may accidentally override the app_name or namespace, and the above calls wouldn't resolve.

    Most of the time, the three-elements-tuple-technique is applied (as described in the django documentation, giving AdminSite as an example). But that enforces a specific namespace, and it's the same as not enforcing one at all.
    So, to avoid user headaches, as this application is reusable, you can give any name you want to your channels.
    Just ensure and remember: don't include the urls.py file with your jackfrost channels inside a namespace. Never. Despite the other urls your application have in that file NEVER do it.
    NEVER
    .

  3. In the forms declarations:

    • Import the needed fields from jackfrost.fields module:

      • AutocompleteCharField (an improved CharField with autocomplete in CharField).
      • AutocompleteModelChoiceField (replaces ModelChoiceField in ForeignKey).
      • AutocompleteModelMultipleChoiceField (replaces ModelMultipleChoiceField in ManyToMany). (You cannot, and you should not, change the widgets). (They can take more **kwargs params, the same as the fields they replace).
    • Declare your ModelForm and override their fields as follows: For each field you want to convert to it's autocomplete version, just override it by declaring it as a new file (matching the model field name) as explained in the last paragraph (which AutocompleteField is intended for which Model field or non-AutoComplete form field). Overriding is as follows:

      #referencing the declared channel myCharField = AutocompleteCharField('myChannel') #referencing the declared channel (or perhaps another channel. it doesn't matter #as long as it's declared as described). widget_attrs contains parameters that #will be explained later. \myForeignKeyField = AutocompleteModelChoiceField('myChannel' [, widget_attrs]) #referencing another declared channel (or perhaps the same...). widget_attrs #contains parameters that will be explained later. myManyToManyField = AutocompleteModelMultipleChoiceField('test_app.languages' [, widget_attrs]) #REMEMBER THAT IN THE FOREIGN KEY AND MANY-TO-MANY FIELDS THERE'S A HIDDEN #INPUT THAT CONTAINS THE REAL ID AND VALUE TO SEND TO THE SERVER.

  4. In the template:

    • DON'T FORGET TO INCLUDE THE form.media REFERENCE IN THE TEMPLATE. OTHERWISE THESE COMPONENTS WILL NOT WORK (these would happen with every django components app -.-'').

Customizing

The components can be customized in several ways.

  • The extra_data_getter can be used to bring more data about the instance (it would be used to custom-render or consider at events).
    • List item rendering function.
      • renderer: A function with signature function(event, key, object).
  • For AutocompleteModelChoiceField component, widget_attrs may be specified containing expressions returning javascript event handlers (i.e. callback functions) whose signatures are:
    • Element assignment event:
      • before_set: A function with signature function(Event, key, object).
        If it returns false, the element is not set.
        Can safely have no return statement.
      • after_set: A function with the same signature that before_set.
        The return value is not taken into account.
    • Element deletion event:
      • before_del: A function with signature function(Event, key).
        If it returns false, the element is not deleted.
        Can safely have no return statement.
      • after_del: A function with the same signature that before_del.
        The return value is not taken into account.
    • List item rendering function.
      • renderer: A function with signature function(event, key, object).It must return an HTML string that will be the content for the rendered element. By not returning any value, the default jquery-ui rendering will be applied. (These events may be alternatively set in client-side by binding to the hidden input these functions in events beforeSet, afterSet, beforeDelete, afterDelete, and render).
  • For AutocompleteModelMultipleChoiceField component, widget_attrs may be specified containing expressions returning javascript event handlers (i.e. callback functions) whose signatures are:
    • Element addition event (before and after):
      • before_set: A function with signature function(Event, key, object).
        If it returns false, the element is not added.
        Can safely have no return statement.
      • after_set: A function with the same signature that before_add.
        The return value is not taken into account.
    • Element removal event (before and after):
      • before_rem: A function with signature function(Event, listbox, key, position).
        If it returns false, the element is not removed.
        Can safely have no return statement.
      • after_rem: A function with the same signature that before_rem.
        The return value is not taken into account.
    • List item rendering function.
      • renderer: A function with signature function(event, key, object). It must return an HTML string that will be the content for the rendered element. By not returning any value, the default jquery-ui rendering will be applied.
        (These events may be alternatively set in client-side by binding to the hidden input these functions in events beforeAdd, afterAdd, beforeRemove, afterRemove, and render).

For each of these methods:

  • Event is a jquery Event.
  • key is the object field value.
  • object is a javascript literal object containing the fields:
    • value = label = The model object's caption
    • key = The key parameter
    • extra = The data as generated by the extra_data_getter if specified "listbox" is the HTML DOM Select component (rendered by the browser) to store the elements.
  • position is the position of the key in the listbox to be removed).

Future Improvements

  • Customizing AJAX error handling (a new event).
  • Customizing the model object caption in a per-channel way and not just the model's __unicode__ method (keeping it as the default option).
  • Initialization events for the components (distinguishing them from the set/add events).
  • Caching/multisourcing.

Limitations

The ManyToMany relationship to use with this AutocompleteModelMultipleChoiceField must NOT have a "through" parameter set.

Other known limitations are explained in the Future Improvements section.

Contact info

You can contact me for bugs, suggestions and doubts to [email protected]. Please put in the subject "jackfrost django app" so I can distinguish that mail from the bulk i usually delete.

About

J.A.C.K.F.R.O.S.T. (Jquery Auto Complete Kit For Remote Object Safe Targeting) is an autocomplete technology based on the jquery ui plugin which allows the search and pick of remote objects (i.e. stored model objects) from the front end as a field value, supporting any dataset/query and having that object at the server side.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published