Django manytomany, imageField and upload_to - python

I have the two models shown below:
class EntryImage(models.Model):
image = models.ImageField(upload_to="entries")
class Entry(models.Model):
code = models.CharField(max_length=70, unique=True)
images = models.ManyToManyField(EntryImage, null=True, blank=True)
As you can see, Entry can have 0 or more images.
My question is: Is it possible to have that kind of
schema and dynamically change the upload_to based on the Entry code?

Well without going too far off, you could make an intermediary M2M table EntryImageDir with the directory name in it. You would link your EntryImages there with a foreign key and you could create the EntryImageDir either with a signal on Entry create or when uploading something.
The documentation for M2M with custom fields is here:
http://www.djangoproject.com/documentation/models/m2m_intermediary/

You can make upload_to a callable, in which case it will be called and passed the instance of the model it is on. However, this may not have been saved yet, in which case you may not be able to query the Entry code.

Related

How to display a list of children objects on detail view for Django Admin?

I have two models: Setting and SettingsGroup.
When someone clicks on a specific SettingsGroup in the Django Admin and the edit/detail page appears I'd like for the child Setting objects to be displayed but as a list not a form.
I know that Django has InlineModelAdmin but this displays the children as editable forms.
My concern isn't with the child objects being editable from the parent object but rather the amount of space it consumes. I'd rather have a list with either a link to the appropriate child record or that changes a particular object to be inline editable.
Here is my Setting model:
class Setting(models.Model):
key = models.CharField(max_length=255, blank=False)
value = models.TextField(blank=True)
group = models.ForeignKey('SettingsGroup', blank=True,
on_delete=models.SET_NULL, null=True)
def __str__(self):
return str(self.key)
And the SettingsGroup model:
class SettingsGroup(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
def __str__(self):
return str(self.name)
The method I don't want to use (or need to find a different way to use) is InlineModelAdmin which appears in my admin.py currently as:
class SettingsGroupInline(admin.StackedInlin):
model = Setting
fk_name = 'group'
#admin.register(SettingsGroup)
class SettingsGroupAdmin(admin.ModelAdmin):
inlines = [ SettingGroupsInline, ]
Here is an example of how I'd like it to work:
There is a MySettings object, an instance of the SettingsGroup model.
There is a CoolSetting object and a BoringSetting object, each an instance of the Setting model.
The CoolSetting object has its group set to the MySettings object.
The BoringSetting object does not have a group set.
When I open the detail/edit view of the Django Admin for the MySettings object I see the normal edit form for the MySettings object and below it the CoolSetting object (but not as a form).
I do not see the BoringSetting object because it is not a child/member/related of/to MySettings.
I have some ideas on how this could be accomplished but this seems like fairly basic functionality and I don't want to go building something if Django (or other existing code) provides a way to accomplish this.
Any ideas?
Why can't you just access the children using something like Setting.objects.filter(group=SettingsGroup.objects.get(name={name}))
If being presented in a template you could pass the SettingsGroup name to the context and iterate over the children and present them however you like.
I may not understand your question exactly so if this is not what you're looking for let me know!

Django AutoSlugField not considering soft deleted instances by Django Safe Delete

In our model we have a name and slug fields. This is how it looks like:
class MyModel(CommonInfo, SafeDeleteModel):
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
slug = AutoSlugField(_('Url'), populate_from='name', unique=True,)
For the slug field we generate an unique slug every time our model is saved.
We are also using Django Safe Delete library to soft delete our model:
Django Safe Delete
Django Autoslug
That means that for example if we create a new instance of our model with Name "My Model" it will auto generate a slug that will look like this: "/my-model".
Now let's say we "soft delete" this instance with the slug "/my-model". In our database there will be a property deleted which contains the date when the model was deleted. We don't show this one in our application, it is completely ignored (because it is soft deleted, that's fine).
The problem is that next time we create another one with the same name "My Model" it will auto generate the slug "/my-model" again, not considering that there is already one (which is soft deleted) with the same name and slug. We would need something like "/my-model-1" or whatever that is unique.
We are missing the connection between the autoslug and the safe-delete libraries, somehow the autoslug needs to know that there might be soft deleted ones and consider them when generating the unique slug.
Any help would be really appreciated and please consider that we are totally new in Django / Python.
if this doesn't work, our workaround will be generating the slug using 2 fields (name & id). It will generate a slug that will look like this: "/my-model/12345" and will be always unique since id is unique.
I think we found it.
We needed to create a new Manager that can see all the instances, even the soft deleted ones:
class MyModelAllManager(SafeDeleteManager):
_safedelete_visibility = DELETED_VISIBLE
Then in our model we pass it to the AutoSlugField function:
class MyModel(CommonInfo, SafeDeleteModel):
# We don't know why but it only works if we create a variable and assign the Manager to it. It doesn't work if we pass the Manager directly to the AutoSlugField function.
all_objects = MyModelAllManager()
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
slug = AutoSlugField(_('Url'), populate_from='name', unique=True, manager=all_objects)
That does the magic.

Using Django ModelForm with non-persistent model.Field

Is there a way to add a field to a Django model class such that:
It doesn't get persisted to the database (i.e. no column in the DB)
It does get rendered by a ModelForm
The widget for that field can be customised
I believe 3. can be done with a custom widget, and 2. will happen if the field inherits from models.Field. However, I haven't found a way to achieve 1. without breaking 2. and 3. I was hoping for a persist=False or db_column=None type of solution.
Scenario:
I'm using this to quickly produce data capture forms by only adding a class to the model, but in order to insert headers for sub sections I still having to edit the template. Was hoping to do the following:
models.py
from django.db import models
class Applicant(models.Model):
sectionA = models.SectionField(help_text="Personal details")
title = models.CharField(max_length=100)
name = models.CharField(max_length=100)
sectionB = models.SectionField(help_text="Banking details")
account = models.CharField(max_length=100)
pin = models.CharField(max_length=100)
In the above example, sectionA and sectionB are instances of a custom model.Field that doesn't actually get persisted but cause a heading to be rendered by the ModelForm and a custom widget
Finally:
I realise this probably violates separation of View and Model.
Other questions have been asked about non-persisting fields but their solutions don't render in a ModelForm
Sort of, Just don't make them a model field, theres no need for them to be.
sectionA = "Personal details"
sectionB = "Banking details"
You can access them via form.instance where you need them, you could even make them a form field instead of a string as I've shown here.

Why can't I create a ForeignKey field to a child class in django?

Class Folder inherits from class Item, which has a foreign key to Folder:
class Item(models.Model):
# some fields
folder = models.ForeignKey('Folder')
class Folder(Item):
# some fields
When I try to run this, I get the error:
app.item: 'folder' has a relation with model Folder, which has either not been installed or is abstract
I thought the correct thing to do here was to put the model name in quotes, which I have done, but it doesn't seem to help. What should I do to make this work?
Edit: Clarified question using meaningful class names
I have Items (A), some of which are Folders (B). I want both classes
to have a reference to at most one Folder
It doesn't make much sense(to me) what you're trying to do but this can be achieved
as follows:
class Item(models.Model):
# some fields
is_folder = models.BooleanField(default=False)
some_other_folder = models.ForeignKey('self', null=True, blank=True)
And then check with python code that if is_folder==False, that some_other_folder
is not None(null).
So actually you don't need 2 models.

Is there a way to define which fields in the model are editable in the admin app?

Assume the following:
models.py
class Entry(models.Model):
title = models.CharField(max_length=50)
slug = models.CharField(max_length=50, unique=True)
body = models.CharField(max_length=200)
admin.py
class EntryAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug':('title',)}
I want the slug to be pre-populated by the title, but I dont want the user to be able to edit it from the admin. I assumed that adding the fields=[] to the admin object and not including the slug would have worked, but it didnt. I also tried setting editable=False in the model, but that also didnt work (infact, stops the page from rendering).
Thoughts?
For this particular case you can override your save method to slugify (it's built-in method, look at django source) the title and store it in slug field. Also from there you can easily check if this slug is indeed unique and change it somehow if it's not.
Consider this example:
def save(self):
from django.template.defaultfilters import slugify
if not self.slug:
self.slug = slugify(self.title)
super(Your_Model_Name,self).save()
I'm not sure what you're asking for IS possible. Your best bet is probably to hide the slug from the admin interface completely by specifying your fieldsets, and than overriding the save method to copy the slug from the tile, and potentially slugifying it...
This Django Snippet does what you want by defining a custom Read-Only Widget. So you define a custom editor for the field which in fact doesn't allow any editing.
This snippet gives you an AutoSlugField with exactly the behavior you are seeking, and adding it to your model is a one-liner.
In addition to overriding save to provide the generated value you want, you can also use the exclude option in your ModelAdmin class to prevent the field from being displayed in the admin:
class EntryAdmin(admin.ModelAdmin):
exclude = ('slug',)

Categories

Resources