I have a situation where I have python scripts that are stored as files that need to be modified from the Admin panel. Currently some of my models can have scripts attached to them:
class Action(PolymorphicModel):
title = models.CharField(max_length=100)
text = BBCodeField('text', blank=True)
action_script = models.FileField(upload_to='action_scripts')
In my Admin panel I can upload a file but I have been asked to make this file editable in-place if the user has the right permissions. I've searched all over for an idiomatic way to do this, and so far I've come up with the following ideas:
Override the FileField widget to include an text edit box and save button. This seems very brute forced
Replace models.FileField with subclass of models.TextField, implement read from file, save to file, but then I lose the upload widget and data still gets stored in DB. This seems dirty because in the database all I'm storing is a filename. FileField feels like the right field type here.
Write a custom ModelForm that includes forms.TextField. Handle save to file using the save method. I'm not sure how to accomplish this because I would have to generate a filename on the fly and I don't have a primary key yet. Maybe some sort of UUID?
I'm leaning against door number 3 right now, but I've only been using Django for a week, so I'm not sure this is the right way to go, or even how to do it. Also I would need to have a custom ModelForm for every model that inherits from Action, which violates DRY.
Further complications:
Not all Actions need scripts. If the textbox is empty, the FileField can just become Null.
Action is subclassed into a handful of derived classes. The editing functionality should extend to each model's Admin panel in a scalable way, like the other fields.
This question seems generic enough that its solution would be useful for those wishing to edit HTML or CSS from the admin, etc, the same as WordPress does. Searching along those lines unfortunately brings up results to do with skinning the admin, which I'm not after. In my situation I'm trying to avoid storing python code in the database for security reasons. The Admin interface is not going to be exposed to the end users so in theory it should be safe, and I get the benefit of caching, compilation and locality (less hits to the DB).
Many thanks.
Related
My specific problem is detailed below, but the main point of my question is this: When I click "Save" on the Django admin form while editing a model, what happens? What is the order of the validation/save process? How can I override these methods to solve my problem below, which is to say updating an intermediate table at the appropriate time as a workaround for the issue I described?
I'm working on a Django application which is facing a PostgreSQL database. The database is relatively large, and I have a lot of many-to-many relationships. The one that I am currently having an issue with is the n:m relationship between People and Sources.
The app is used mainly by non-technical people to add to the database through Django's built-in admin feature. The primary issue that I was having when I started working on the app was severe page slowdown when editing a Person on the admin site. If the person had too many Sources, each drop-down in the TabularInline representation of sources loaded thousands of entries. I found a workaround and overrode the get_formfield_for_foreignkey() method as follows:
def formfield_for_foreignkey(self, db_field, request, **kwargs):
field = super(SourceMaterialInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
# We will overwrite this method to ONLY return relevant materials on this person
materials = SourceMaterial.objects.all()
materials_p = SourceMaterial.people.through.objects.all()
person_ID = get_ID_from_path(request.path)
if not_integer(person_ID):
return field
materials_pid = materials_p.filter(person_id=person_ID)
# The next step is to use these relevant materials to obtain the SourceMaterial objects from the SourceMaterial table
relevant_ids = []
for mat in materials_pid:
relevant_ids += [mat.sourcematerial_id]
relevant_materials = materials.filter(id__in=relevant_ids)
# now we can set the queryset so that only relevant materials are included
field.queryset = relevant_materials
return field
So this accomplishes what I wanted. When you go to edit a Person object through the Admin page, the dropdown menus to select sources only load and display Sources that belong to that Person.
The problem this creates, however, is when I go to add a new Source via the Person change form. I would like to keep this functionality, but it is now broken.
If I go to add a new source, a pop-up window appears. I select the file on my machine, give it a name and click add. At this point it is added to the Sources table, but not to the intermediate table linking it to this Person. When I go to save the Person, the form asks me to correct this error: Select a valid choice. That choice is not one of the available choices.
Here is the pop-up: Add new source material pop-up for clarity
This makes sense, as there is no established link between the Person and the Source yet. So instead I try adding the Person ID as well when adding a source this way, and the error is: Sourcematerial-person relationship with this Sourcematerial and Person already exists. I'm assuming this is because when I add the Source with the Person ID, the intermediate table is populated appropriately, and when I go to save, it tries to add the source again, because the form detects it as being new.
I'm hoping I can override form validation/saving so that I can keep functionality of adding a Source to the database this way. If anybody has any suggestions as to what exactly I could override (I was very confused by the docs) or another work around, that'd be great. Thanks!
I've been playing with django import-export because it seems to be the obvious choice for anything import/export related in django and it's fantastic. Trouble is I can't find much in the way of docs on the import side of things and I'd like to be able to use it to import data through and cope with table relationships.
For example I've got a system for a cycling event, and an event has a table of various places managed by a user. I'd like that user to be able to import a file of details for people who will fill those places.
The places the user has have a FK to another table for cyclists in this event and the uploaded file would contain cyclists details. However the cyclist objects may not exist yet.
I've been working with django-import-export from an admin perspective, targeting specific model objects by ID from a CSV, but is it possible to cope with object relationships?
My thought was that from the view where a user can see their places in an event and add a cyclist to each one individually or upload a CSV file they could also download a CSV file. This would contain the ID for each place and a column for each piece of cyclist information I want to be supplied.
I know from the export side of this I was able to specify place__cyclist__name to get the name of a cyclist associated with a place. Is the same possible for importing data?
It's answered here,
Adding foreignKey widget to django-import-export
And here
import m2m relation in django-import-export
For more details: django-import-export Widgets
I would like have additional settings tied to each user in my application (beyond, is_staff, is_admin etc etc). Basically I'd like to have different settings to customize their user experience (ie: don't show tooltips, how many rows to display in results tables, other flags for turning things on or off).
Are there best practices for adding these types of settings, or example model to do this without touching the django user object (in the past when i needed a quick user property, i just added it to my django source code, but obviously know that this is a horrible idea).
So when someone sucessfully logs in, I would grab the settings for the user and add them to the session.
I wasn't sure if there was a pretty way, or best practice for doing this.
As already said, use UserProfile. To store many flags in the same field there's django-bitfield.
Either put them in the user profile model, or create another model with a one-to-one to User.
I have a Django admin interface that is used almost solely as a gui form for making changes to a single postgresql table. There's also a Python script that's currently run manually from the command line whenever a change is made to the database, & I'd like to hook that up so it runs whenever someone hits "save" after making a change to a row of the table via the admin interface. If this was an entry in views.py, it looks like I'd import the script as a module and run its main function from the view (ie, Can Django use "external" python scripts linked to other libraries (NumPy, RPy2...)). I'm not sure, however, how to do this in the admin interface.
How is admin.py similar/different to a regular entry in views.py?
Where do I put the import/call to the external script - somewhere in the model, somewhere in admin.py?
I'm familiar with Python, but am fairly new to (& somewhat mystified by) "web stuff" (ie, frameworks like Django), & I'm not even sure if I'm asking this question very clearly, because I'm still a little fuzzy on the view/model concept ...
Edit: Turns out I had, in fact, found the solution by reading the documentation/tutorial, but assumed there was a difference with admin stuff. As Keith mentioned in the comments, I'm now running into permissions issues, but I guess that's a separate problem. So thanks, & maybe I'll stop second guessing myself ...
Generally, things you want to happen at 'save' time are either
Part of the model.
If so, you override the model's save method: http://docs.djangoproject.com/en/1.3/ref/models/instances/#saving-objects
You can do anything in that save method.
Part of the view function.
If so, you either extend the admin interface (not so easy), or you write your own.
One thing you might consider is defining the save_model method in your ModelAdmin. This will get executed when someone saves from the admin (but not when someone does a save outside of the admin). This approach might depend on what your requirements are, but should give you the necessary hook when doing the save from the admin.
In admin.py
class MyModelAdmin(admin.ModelAdmin):
model = models.MyModel
def save_model(self, request, obj, form, change):
# you can put custom code in here
obj.save()
I would like to create a single page in the admin site of django where I can change some global variables of the website (title of the website, items in the navigation menu, etc). At the moment I have them coded as context processors but I would like to make them editable. Something similar to what happens in WordPress.
Is this possible?
I can store the data in the databse, but can I have a link in the admin site that goes straight to the first document record and doesnt allow the creation of multiple records (they wouldnt make sense)
Instead of creating a model in the database, would it be possible to change some context_processor from the admin site (I think this would be best)
django-preferences does exactly what you are looking for. The implementation is a bit hacky (particularly the setting of __module__ on the model class to trick Django into thinking it was loaded from a different app), but it works.
This sounds like what the sites framework is intended to help with.
http://docs.djangoproject.com/en/stable/ref/contrib/sites/
"It’s a hook for associating objects and functionality to particular Web sites, and it’s a holding place for the domain names and “verbose” names of your Django-powered sites."
The docs make it sound like it's only good for multiple sites, but it's a great place to put stuff in a single-site-per-django model too.
There's an app called django-values that allows you storing of specific settings in the database.