Django raw_id_fields don't display tables the way I expect, am I doing something wrong? I'm trying to use a raw_id_fields widget to edit a ForeignKey field as follows:
#models.py
class OrderLine(models.Model):
product = ForeignKey('SternProduct')
class SternProduct(models.Model):
def __unicode__(self): return self.product_num
product_num = models.CharField(max_length=255)
#admin.py
#import and register stuff
class OrderLineAdmin(admin.ModelAdmin):
raw_id_fields=('product')
I get the little textbox and magnifier widget as expected, but clicking the magnifier gives me this:
flickr.com/photos/28928816#N00/5244376512/sizes/o/in/photostream/
(sorry, can't post more than one hyperlink apparently)
I thought I would get something closer to the changelist page c/w columns, filters and search fields. In fact, that's apparently what others get.
Any thoughts about how to enable the more featureful widget?
Ah, OK, this should have been obvious, but it isn't explained in the Django docs. The list that appears in the raw_id_fields popup uses the same options as the admin object for the referenced model. So in my example, to get a nice looking popup I needed to create a SternProductAdmin object as follows:
class SternProductAdmin(admin.ModelAdmin):
list_display = ('__unicode__', 'drawing_number', 'drawing_revision',)
list_filter = ('drawing_number',)
search_fields = ('drawing_number',)
actions = None
Hopefully this will help others in the future.
Related
I have a custom contact form for which I create a sent_time field using auto_now_add to save the time when the user had sent the message.
I am able to list all the information on the listing view of the admin panel however when I try to enter a specific message I hit the following error:
'sent_time' cannot be specified for GeneralContact model form as it is a non-editable field
My attempt to make the fields readonly in the ModelAdmin results in the same error
class GeneralContactAdmin(ModelAdmin):
"""
Admin model for general correspondence via
the main contact form on the information page
"""
model = GeneralContact
list_display = GeneralContact.__all__
search_fields = GeneralContact.__all__
readonly_fields = GeneralContact.__all__
ordering = ('-sent_time',)
list_filter = ('sent_time', 'has_response')
Surely it is possible to be displayed only, perhaps I've done something incorrectly in my models?
Here is the base model I use for the contact model
class ContactFormBase(models.Model):
__all__ = (
'sent_time', 'sender_name', 'sender_email',
'sender_message', 'has_response', 'responded_on'
)
sent_time = models.DateTimeField(auto_now_add=True)
sender_name = models.CharField()
sender_email = models.EmailField()
sender_message = models.TextField()
has_response = models.BooleanField(
default=False,
help_text='Select whether this message has been replied to by an admin.',
)
responded_on = models.DateTimeField(blank=True, null=True)
panels = [
FieldRowPanel([
FieldPanel('sender_name'),
FieldPanel('sender_email'),
]),
FieldPanel('sent_time'),
FieldPanel('sender_message'),
FieldRowPanel([
FieldPanel('has_response'),
FieldPanel('responded_on'),
])
]
class Meta:
abstract = True
ordering = ['-sent_time',]
The actual class being used is rather plain, perhaps something needs to be done here to allow display of readonly fields?
class GeneralContact(ContactFormBase, models.Model):
panels = ContactFormBase.panels
class Meta:
verbose_name = 'General Contact Entry'
verbose_name_plural = 'General Contact Entries'
In the list view all the information is able to be displayed. In the editing view, ideally there would be all of the information about the message and sender as readonly fields and an option for the admin to change the has_response value based on whether someone has responded or not.
In what way could I achieve this?
update
After seeing this Q&A I have changed the auto_now_add to use django.utils.timezone.now as the default on the sent_time attribute and life seems better, the error from the start of the question is gone and the edit view loads up entirely. However, now all the fields are editable which is not desirable.
Looking into the ModelAdmin class provided by Wagtail it appears that readonly_fields isn't available and perhaps only a feature of the django admin class of the same name. So I'm not sure what to do here. Wagtails HelpPanel type of output is what I'm looking for, and I had an idea to use that to display the data but I'm not sure what that looks like or even how it'd be done as I'm just learning django and wagtail.
update 2
Attempted to use HelpPanel instead of FieldPanel in order to try display the values but seems as if the HelpPanel doesn't retrieve the value of the attributes. Checking through these docs I see the mention of things like djangos readonly_field is not included which confirms why one of my former attempts didn't work but I did find mention of inspect_view_enabled which displays the values in a read only fashion and after trying it out it looks very much how I was trying to get it, alas, nothing there is editable which makes sense but I am getting closer.
I am wondering if a good solution would be to override the view or template used for GeneralContactAdmin but unsure if that's the right way to go about it just to output some text for one class.
A simpler solution is to keep the inspect view and only add the has_response to the edit view, but two views, one of which would only be a checkbox is not a nice for UX.
Surely there is a better way to solve this?
I am trying to develop a ckeditor comment box in Django. I successfully added the comment box. But now I am facing the issue in editing the comment part. What I am trying to do is when the user click on the edit button near to the comment, a modal popup appears and editing should happens there. The issue is the ckeditor is not showing in the modal. Instead of ckeditor a normal text field with data from the database is showing. In the console it showing "editor-element-conflict" error. Is there any solution for this?
I've figured out!
It happens because you have a few ckeditor fields on the page, and they have the same ID, and CKEditor gets confused because it finds a few elements with the same ID.
Solution: change IDs dynamically when the page is being generated.
I don't know the structure of your model, but I can assume that your form is defined like this:
class Comment(models.Model):
text = models.TextField()
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = “__all__”
Then you need to change it like this:
from ckeditor.widgets import CKEditorWidget
class CommentForm(forms.ModelForm):
base_textarea_id = "id_text"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.textarea_id_counter = 0
self.fields['text'].widget = CKEditorWidget(attrs={'id': self.get_textarea_next_id})
def get_textarea_next_id(self):
result = self.base_textarea_id + str(self.textarea_id_counter)
self.textarea_id_counter += 1
return result
class Meta:
model = Comment
fields = “__all__”
Of course, it’s based on very simplified model example, but I hope you’ve got the point.
In the change list of the django admin, I want to use list_editable to display a django-autocomplete widget for a ManyToManyField.
I found something similar here: list_editable and widgets
Normally including a ManyToManyField in list_display raises an ImproperlyConfigured exception, eg:
""'BookAdmin.list_display[2]', 'author' is a ManyToManyField which is not supported."
I (perhaps unwisely) removed 3 lines from contrib/admin/validate.py to bypass the exception. :)
I now have it spitting out the following, which is close(?) but no cigar.
<django.db.models.fields.related.ManyRelatedManager object at 0x1032a85d0>
Any thoughts on how to proceed? Is there a better way of doing this?
Here's what I have at the moment: (AuthorAutocomplete is working fine in the regular admin form)
class AuthorAutocomplete(AutocompleteSettings):
search_fields = ('first_name', 'last_name')
class BookAdmin(AutocompleteAdmin, admin.ModelAdmin):
def get_changelist_form(self, request, **kwargs):
kwargs.setdefault('form', AuthorAutocompleteForm)
return super(BookAdmin, self).get_changelist_form(request, **kwargs)
list_display = ['isbn', 'title', 'author', 'publisher']
#...
class AuthorAutocompleteForm(forms.ModelForm):
class Meta:
model = Book
author = AuthorAutocomplete
Thanks!
To get the values for a ManyToMany field in your own code so you can display their values you can do the following. I'm using list_display as an example.
class TestAdmin(admin.ModelAdmin):
def get_some_value(self):
return ", " . join([x.__str__() for x in self.manytomany_field.all()])
get_some_value.short_description = 'Title' # sets column header title
list_display = ('get_some_value',) # define Model's fields to be presented on admin changelist
where manytomany_field is the name you gave your manytomany_field and get_some_value is a method within the class which is assigned a short_description.
Silly me. I thought that William's manytomany_field was a built-in ModelAdmin object. So I ran his code as is (after putting in the missing parenthesis after join).
Strange to say, it ran without producing an error message. But (None) appeared when I was supposed to get values. And I thought it was strange that I couldn't find anything when I Googled "django model.Admin.manytomany_field". Hah!
So eventually I realized that I was to put the NAME of the many-to-many field in place of manytomany_field. It works!
Two of my model's fields are "title" and "summary". Right now, "title" is the first field in the ModelAdmin's list_display, which makes it link to the change page. I have some other fields in list_display as well.
I'd like to make the admin change list page display "summary" under "title" in plain, unlinked text, in the same column as "title". Is this possible? I'm using Django 1.1.
Thanks
Kind of. You can setup your own custom list_display objects to use. So for example, in your case you may do something like so:
def title_and_summary(obj):
return "%s %s" % (obj.title, obj.summary)
Then within your admin class:
class MyAdmin(admin.ModelAdmin):
list_display = (title_and_summary,)
More information can be found on the list_display documentation.
A have 3 models: Project, Image and Video with ManyToManyField relation:
class Project(models.Model):
images = models.ManyToManyField('Image', through='Project_Images')
video = models.ManyToManyField('Video', through='Project_Video')
class Image(models.Model):
original = models.ImageField()
projects = models.ManyToManyField('Project', through='Project_Images')
class Video(models.Model):
projects = models.ManyToManyField('Project', through='Project_Video')
I configure project's admin form with inline forms of Images and Videos linked to current project:
class ProjectAdmin(admin.ModelAdmin):
inlines = [VideoInline, ImagesInline]
class ImagesInline(admin.TabularInline):
model = Project_Images
raw_id_fields = ['project','image']
class VideoInline(admin.TabularInline):
model = Project_Video
raw_id_fields = ['project','video']
But inline table with simple select field and delete checkbox is much miserable for me, and I want to show here previews of images or video (youtube). I solve this for images with help of AdminImageWidget:
class ImageForm(forms.ModelForm):
class Meta:
model = Image
preview = forms.ImageField(widget=AdminImageWidget())
def __init__(self, *args, **kwargs):
super(ImageForm, self).__init__(*args, **kwargs)
try:
image = Image.objects.get(id=self.instance.image_id)
self.fields["preview"].initial = image.original
except:
pass
class ImagesInline(admin.TabularInline):
.....
form = ImageForm
Is it best way to do this? In my case I don't need file upload input, only image-preview in inline form table. I need also preview for youtube video, should I write my own widget for displaying video and apply it to some fake field ?
It's strange to solve this issue by widget for unnecessary fake field. Or is it normal way?
Any help will be very much appreciated!
Thanks!
You should create a widget similar to AdminImageWidget but that displays only the image, not the upload box. To apply that widget you don't need a custom Form class or a fake field, just use formfield_overrides on your ImageInline:
class ImageInline(admin.TabularInline):
...
formfield_overrides = { models.ImageField: {'widget': YourPreviewWidget}}
EDIT: Oops, didn't fully process the ManyToManyField issue - you're displaying inlines for the "through" table, not the table with the actual ImageFields. Given that, what you're doing now may be not such a bad solution. The alternative I can think of would be to write a specialized replacement widget for the Select, that knows how to display both the select box and a preview image for the currently selected Image object. That way you could avoid the need for the fake extra field.