Django models as optgroup choices - django

I am trying to get my installed models as opt group choices.
I have to do it in my model, not form.
so far I tried.
def get_installed_model_choices():
returnval = []
for app in settings.SYSTEM_INSTALLED_APPS:
app_config = apps.get_app_config(app)
app_models = app_config.get_models()
model_choices = []
for model in app_models:
model_choices.append([model._meta.model_name, model._meta.verbose_name])
returnval.append([app_config.verbose_name, model_choices])
print returnval
return returnval
And in my model field.
model = custom_model_fields.PanNoneBlankCharField(choices = get_installed_model_choices(),verbose_name=_('model'),
max_length=20)
I get this error.
"Models for app '%s' haven't been imported yet." % self.label)
Second approach:
This is comething I saw on the internet,
def __init__(self, *args, **kwargs):
super(PanUserModelPermissions, self).__init__(*args, **kwargs)
self._meta.get_field('model')._choices = \
lazy(get_installed_model_choices, list)()
This didnt work either, had no affect
I dont want choices to be static, is there anyway to achieve this?

if you just want to choice from your models,you can use content_type as:
from django.contrib.contenttypes.models import ContentType
content_type = models.ForeignKey(ContentType,
null=True,
blank=True,
verbose_name=u'content_type ')
more info here.

Related

Django-taggit how to modify to have same tag added by different user

I am trying to modify django-taggit to allow the same tag to be added by separate teams.
I have modified django-taggit Model so that it has user and team_id values added when a user adds a new tag to taggig_tag table. This also adds user and team_id values to taggit_taggeditems table.
The goal is to allow teams to edit or delete their own tags and that should not affect other teams, so different teams need to have their own separate sets of tags.
In my modified scenario, the tag name and team_id constitute a distinct tag. I expect i can test team_id or concatenate it to the tag name before django-taggit tests for distinct tags. But do not see where django-taggit does that.
Question: Where in the django-taggit code does it look for duplicate tag values?
`apps.py`
`forms.py`
`managers.py`
`models.py`
`utils.py`
`views.py`
My modified django-taggit Model code is below.
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import IntegrityError, models, router, transaction
from django.utils.text import slugify
from django.utils.translation import gettext, gettext_lazy as _
from django.conf import settings
### MODIFICATION: added django CRUM to get request user
from crum import get_current_user
try:
from unidecode import unidecode
except ImportError:
def unidecode(tag):
return tag
class TagBase(models.Model):
### MODIFICATION: added team and user to model, removed unique=True
name = models.CharField(verbose_name=_("Name"), max_length=100)
slug = models.SlugField(verbose_name=_("Slug"), max_length=100)
team_id = models.CharField(max_length=10, blank=False, null=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
def __gt__(self, other):
return self.name.lower() > other.name.lower()
def __lt__(self, other):
return self.name.lower() < other.name.lower()
class Meta:
abstract = True
def save(self, *args, **kwargs):
### MODIFICATION: added team and user to taggit_taggeditem model
### get request user with django CRUM get_current_user()
self.user = get_current_user()
self.team_id = get_current_user().team_id
if self._state.adding and not self.slug:
self.slug = self.slugify(self.name)
using = kwargs.get("using") or router.db_for_write(
type(self), instance=self
)
# Make sure we write to the same db for all attempted writes,
# with a multi-master setup, theoretically we could try to
# write and rollback on different DBs
kwargs["using"] = using
# Be oportunistic and try to save the tag, this should work for
# most cases ;)
### MODIFICATION: remove IntegrityError try/except for unique
which is removed
#try:
with transaction.atomic(using=using):
res = super().save(*args, **kwargs)
return res
#except IntegrityError:
# pass
### MODIFICATION: remove slugs create as no longer checking for
duplicate slugs
# Now try to find existing slugs with similar names
#slugs = set(
# self.__class__._default_manager.filter(
# slug__startswith=self.slug
# ).values_list("slug", flat=True)
#)
i = 1
#while True:
# slug = self.slugify(self.name, i)
# if slug not in slugs:
# self.slug = slug
# # We purposely ignore concurrecny issues here for now.
# # (That is, till we found a nice solution...)
# return super().save(*args, **kwargs)
# i += 1
while True:
slug = self.slugify(self.name, i)
#if slug not in slugs:
self.slug = slug
# We purposely ignore concurrecny issues here for now.
# (That is, till we found a nice solution...)
return super().save(*args, **kwargs)
i += 1
else:
return super().save(*args, **kwargs)
def slugify(self, tag, i=None):
slug = slugify(unidecode(tag))
if i is not None:
slug += "_%d" % i
return slug
class Tag(TagBase):
class Meta:
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
app_label = "taggit"
class ItemBase(models.Model):
def __str__(self):
return gettext("%(object)s tagged with %(tag)s") % {
"object": self.content_object,
"tag": self.tag,
}
class Meta:
abstract = True
#classmethod
def tag_model(cls):
field = cls._meta.get_field("tag")
return field.remote_field.model
#classmethod
def tag_relname(cls):
field = cls._meta.get_field("tag")
return field.remote_field.related_name
#classmethod
def lookup_kwargs(cls, instance):
return {"content_object": instance}
class TaggedItemBase(ItemBase):
tag = models.ForeignKey(
Tag, related_name="%(app_label)s_%(class)s_items",
on_delete=models.CASCADE
)
class Meta:
abstract = True
#classmethod
def tags_for(cls, model, instance=None, **extra_filters):
kwargs = extra_filters or {}
if instance is not None:
kwargs.update({"%s__content_object" % cls.tag_relname():
instance})
return cls.tag_model().objects.filter(**kwargs)
kwargs.update({"%s__content_object__isnull" % cls.tag_relname():
False})
return cls.tag_model().objects.filter(**kwargs).distinct()
class CommonGenericTaggedItemBase(ItemBase):
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
verbose_name=_("Content type"),
related_name="%(app_label)s_%(class)s_tagged_items",
)
content_object = GenericForeignKey()
### MODIFICATION: added team and user to taggit_taggeditem model
team_id = models.CharField(max_length=10, blank=False, null=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
on_delete=models.DO_NOTHING)
class Meta:
abstract = True
#classmethod
def lookup_kwargs(cls, instance):
return {
"object_id": instance.pk,
"content_type": ContentType.objects.get_for_model(instance),
### MODIFICATION: added team and user to taggit_taggeditem model
"user": get_current_user(),
"team_id": get_current_user().team_id,
}
#classmethod
def tags_for(cls, model, instance=None, **extra_filters):
tag_relname = cls.tag_relname()
kwargs = {
"%s__content_type__app_label" % tag_relname:
model._meta.app_label,
"%s__content_type__model" % tag_relname: model._meta.model_name,
}
if instance is not None:
kwargs["%s__object_id" % tag_relname] = instance.pk
if extra_filters:
kwargs.update(extra_filters)
return cls.tag_model().objects.filter(**kwargs).distinct()
class GenericTaggedItemBase(CommonGenericTaggedItemBase):
object_id = models.IntegerField(verbose_name=_("Object id"),
db_index=True)
class Meta:
abstract = True
class GenericUUIDTaggedItemBase(CommonGenericTaggedItemBase):
object_id = models.UUIDField(verbose_name=_("Object id"), db_index=True)
class Meta:
abstract = True
class TaggedItem(GenericTaggedItemBase, TaggedItemBase):
class Meta:
verbose_name = _("Tagged Item")
verbose_name_plural = _("Tagged Items")
app_label = "taggit"
### MODIFICATION: added team_id and user to taggit_taggeditems table
constraints
index_together = [["content_type", "object_id", "team_id", "user"]]
unique_together = [["content_type", "object_id", "tag", "team_id",
"user"]]

Auto Generated Slugs in Django Admin

I have an app that will one day allow front-end crud, which will create the slug with slugify. Right now though, all the object creation is being done in the admin area and I was wondering if there is a way to auto generate slugs while creating and saving an object from within admin?
Here is the method for slugify for the front-end; not sure if its even relevant. Thank you.
def create_slug(instance, new_slug=None):
slug = slugify(instance.title)
if new_slug is not None:
slug = new_slug
qs = Veteran.objects.filter(slug=slug).order_by('-id')
exists = qs.exists()
if exists:
new_slug = '%s-%s' % (slug, qs.first().id)
return create_slug(instance, new_slug=new_slug)
return slug
Having just used this on another answer, I have exactly the right code in my clipboard. I do exactly this for one of my models:
from django.utils.text import slugify
class Event(models.Model):
date = models.DateField()
location_title = models.TextField()
location_code = models.TextField(blank=True, null=True)
picture_url = models.URLField(blank=True, null=True, max_length=250)
event_url = models.SlugField(unique=True, max_length=250)
def __str__(self):
return self.event_url + " " + str(self.date)
def save(self, *args, **kwargs):
self.event_url = slugify(self.location_title+str(self.date))
super(Event, self).save(*args, **kwargs)
Above solutions break validation in the Django Admin interface. I suggest:
from django import forms
from django.http.request import QueryDict
from django.utils.text import slugify
from .models import Article
class ArticleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ArticleForm, self).__init__(*args, **kwargs)
# Ensure that data is a regular Python dictionary so we can
# modify it later.
if isinstance(self.data, QueryDict):
self.data = self.data.copy()
# We assume here that the slug is only generated once, when
# saving the object. Since they are used in URLs they should
# not change when valid.
if not self.instance.pk and self.data.get('title'):
self.data['slug'] = slugify(self.data['title'])
class Meta:
model = Article
exclude = []

Using Django-taggit with django-rest-framework, i'm not able to save my tags

I'm trying to figure it out why when i submit my form, my tags are not saved in my db. Pretty new with the django-rest-framework and Django-taggit too, i think i'm doing something wrong :)
First, before making my API with the rest-framework, i was using a generic view (CreateView and UpdateView) to register/validate my event. It was working fine but i decided to go further and try to build an API since i'm using Angularjs now.
Now my model event is created but without my tag and i have some errors. I put some code and i'll describe my errors after.
events/models.py
class Event(models.Model):
[...]
title = models.CharField(max_length=245, blank=False)
description = models.TextField(max_length=750, null=True, blank=True)
start = models.DateTimeField()
end = models.DateTimeField()
created_at = models.DateTimeField(editable=False)
updated_at = models.DateTimeField(editable=False)
slug = AutoSlugField(populate_from='title', unique=True, editable=False)
expert = models.BooleanField(choices=MODE_EXPERT, default=0)
home = models.BooleanField(choices=HOME, default=0)
nb_participant = models.PositiveSmallIntegerField(default=1)
price = models.PositiveSmallIntegerField(default=0)
cancelled = models.BooleanField(default=0)
user = models.ForeignKey(User, editable=False, related_name='author')
address = models.ForeignKey('Address', editable=False, related_name='events')
participants = models.ManyToManyField(User, related_name='participants', blank=True, editable=False,
through='Participants')
theme_category = models.ForeignKey('EventThemeCategory', unique=True, editable=False)
tags = TaggableManager(blank=True)
class Meta:
db_table = 'event'
def save(self, *args, **kwargs):
if not self.pk:
self.created_at = timezone.now()
self.updated_at = timezone.now()
super(Event, self).save(*args, **kwargs)
[...]
i'm using the serializers.HyperlinkedModelSerializer.
api/serializer.py
from taggit.models import Tag
class TagListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Tag
fields = ('url', 'id', 'name')
class EventSerializer(serializers.HyperlinkedModelSerializer):
address = AddressSerializer()
user = UserSerializer(required=False)
tags = TagListSerializer(blank=True)
class Meta:
model = Event
fields = ('url', 'id', 'title', 'description', 'start', 'end', 'created_at', 'updated_at', 'slug', 'expert','home', 'nb_participant', 'price', 'address', 'user', 'theme_category', 'tags')
depth = 1
api/views/tags_views.py
from rest_framework import generics
from api.serializers import TagListSerializer
from taggit.models import Tag
class TagsListAPIView(generics.ListCreateAPIView):
queryset = Tag.objects.all()
model = Tag
serializer_class = TagListSerializer
class TagsDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
queryset = Tag.objects.all()
model = Tag
serializer_class = TagListSerializer
api/views/events_views.py
class EventListAPIView(generics.ListCreateAPIView):
queryset = Event.objects.all()
model = Event
serializer_class = EventSerializer
paginate_by = 100
def pre_save(self, obj):
"""
Set the object's owner, based on the incoming request.
"""
obj.user = self.request.user
return super(EventListAPIView, self).pre_save(obj)
api/urls.py
url(r'^events/(?P<slug>[0-9a-zA-Z_-]+)/$', EventDetailAPIView.as_view(), name='event-detail'),
So first when i call /api/events/name-of-my-event the API send me the good resource with my tags on it. The GET method is working fine.
I was thinking that rest-framework follow the query set. So if i can get the resource with with all my tags why when i use POST my tags are not register ?
Actually i have two problems with the POST method:
first one if i send a tag which i have already created, he send me an error saying that the tag must be unique. I understand that, i don't want to create a new one, i just want it to be linked with my object. I don't have this problem when i use the generic view (it's done by magic :) and all is working fine)
Secondly, when i try to create a new tag, my new event is saved but without my tags.
You can see the response received by angularjs for my tag... He send me the name of the tag but without id, url (hyperlinked). When i checked my db the tag has not been created.
I think i have to make a custom get_queryset(self) in my tags_views but i'm not sure.
I'll will continue to investigate. If someone have already to that and have some advise, i'll be very API. Thanks.
meet the same question. But I just want to save the tag list directly by TaggableManager (without TagListSerializer and TagsListAPIView). My solution is:
class MyModel(models.Model):
...
tags = TaggableManager(blank=True)
def get_tags_display(self):
return self.tags.values_list('name', flat=True)
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
...
tags = serializers.Field(source='get_tags_display') # more about: http://www.django-rest-framework.org/api-guide/fields#generic-fields
...
class MyModelViewSet(viewsets.ModelViewSet):
...
def post_save(self, *args, **kwargs):
if 'tags' in self.request.DATA:
self.object.tags.set(*self.request.DATA['tags']) # type(self.object.tags) == <taggit.managers._TaggableManager>
return super(MyModelViewSet, self).post_save(*args, **kwargs)
The post data of tags data will be ['tagA', 'tagB',...], the TaggableManager will handle it. Thx.
For DRF>3.1, you just need to override create and update in your ModelSerializer class:
class StringListField(serializers.ListField): # get from http://www.django-rest-framework.org/api-guide/fields/#listfield
child = serializers.CharField()
def to_representation(self, data):
return ' '.join(data.values_list('name', flat=True)) # you change the representation style here.
class MyModelSerializer(serializers.ModelSerializer):
tags = StringListField()
class Meta:
model = models.MyModel
def create(self, validated_data):
tags = validated_data.pop('tags')
instance = super(MyModelSerializer, self).create(validated_data)
instance.tags.set(*tags)
return instance
def update(self, instance, validated_data):
# looks same as create method
I used to follow the following ways to serialize taggit objects but currently django-taggit provide a built in serializer https://github.com/jazzband/django-taggit/blob/master/taggit/serializers.py and it was vendor from the package I mentioned previously.
"""
Django-taggit serializer support
Originally vendored from https://github.com/glemmaPaul/django-taggit-serializer
"""
import json
# Third party
from django.utils.translation import gettext_lazy
from rest_framework import serializers
class TagList(list):
def __init__(self, *args, **kwargs):
pretty_print = kwargs.pop("pretty_print", True)
super().__init__(*args, **kwargs)
self.pretty_print = pretty_print
def __add__(self, rhs):
return TagList(super().__add__(rhs))
def __getitem__(self, item):
result = super().__getitem__(item)
try:
return TagList(result)
except TypeError:
return result
def __str__(self):
if self.pretty_print:
return json.dumps(self, sort_keys=True, indent=4, separators=(",", ": "))
else:
return json.dumps(self)
class TagListSerializerField(serializers.Field):
child = serializers.CharField()
default_error_messages = {
"not_a_list": gettext_lazy(
'Expected a list of items but got type "{input_type}".'
),
"invalid_json": gettext_lazy(
"Invalid json list. A tag list submitted in string"
" form must be valid json."
),
"not_a_str": gettext_lazy("All list items must be of string type."),
}
order_by = None
def __init__(self, **kwargs):
pretty_print = kwargs.pop("pretty_print", True)
style = kwargs.pop("style", {})
kwargs["style"] = {"base_template": "textarea.html"}
kwargs["style"].update(style)
super().__init__(**kwargs)
self.pretty_print = pretty_print
def to_internal_value(self, value):
if isinstance(value, str):
if not value:
value = "[]"
try:
value = json.loads(value)
except ValueError:
self.fail("invalid_json")
if not isinstance(value, list):
self.fail("not_a_list", input_type=type(value).__name__)
for s in value:
if not isinstance(s, str):
self.fail("not_a_str")
self.child.run_validation(s)
return value
def to_representation(self, value):
if not isinstance(value, TagList):
if not isinstance(value, list):
if self.order_by:
tags = value.all().order_by(*self.order_by)
else:
tags = value.all()
value = [tag.name for tag in tags]
value = TagList(value, pretty_print=self.pretty_print)
return value
class TaggitSerializer(serializers.Serializer):
def create(self, validated_data):
to_be_tagged, validated_data = self._pop_tags(validated_data)
tag_object = super().create(validated_data)
return self._save_tags(tag_object, to_be_tagged)
def update(self, instance, validated_data):
to_be_tagged, validated_data = self._pop_tags(validated_data)
tag_object = super().update(instance, validated_data)
return self._save_tags(tag_object, to_be_tagged)
def _save_tags(self, tag_object, tags):
for key in tags.keys():
tag_values = tags.get(key)
getattr(tag_object, key).set(tag_values)
return tag_object
def _pop_tags(self, validated_data):
to_be_tagged = {}
for key in self.fields.keys():
field = self.fields[key]
if isinstance(field, TagListSerializerField):
if key in validated_data:
to_be_tagged[key] = validated_data.pop(key)
return (to_be_tagged, validated_data)
http://blog.pedesen.de/2013/07/06/Using-django-rest-framework-with-tagged-items-django-taggit/
With the release of the Django Rest Framework 3.0, the code for the TagListSerializer has changed slightly. The serializers.WritableField was depreciated in favour for serializers.Field for the creation of custom serializer fields such as this. Below is the corrected code for Django Rest Framework 3.0.
class TagListSerializer(serializers.Field):
def to_internal_value(self, data):
if type(data) is not list:
raise ParseError("expected a list of data")
return data
def to_representation(self, obj):
if type(obj) is not list:
return [tag.name for tag in obj.all()]
return obj
I now use the bulit in taggit serializer which was taken from https://github.com/glemmaPaul/django-taggit-serializer library.
I had a bunch of errors but i found a way to resolve my problem. Maybe not the best as i'm pretty new with all of this but for now it works.
I'll try to describe all my errors maybe it'll help someone.
First my angularjs send a json which match exatly the queryset
So for example with my model events below, angularjs send to the API:
Now let's begin with all my errors:
"A tag with this name already exist"
When i re-use a tag i have this error. Don't know why because with a classic validation without the API, all is working fine.
With a new tag nothing is saved too.
When i try to use a new tag on my event event model nothing is saved on the database. Angularjs received a response with the tag name but with an id of null (see the pitcure on my original question)
"AttributeError: 'RelationsList' object has no attribute 'add'"
Now i'm trying to think that to register my tags i need to have an instance of event already created. Thanks to that i will be able to add my tag on it like it's describe in the doc.
apple.tags.add("red", "green", "fruit")
So i decided to add a post_save in my events_views.py:
class EventListAPIView(generics.ListCreateAPIView):
queryset = Event.objects.all()
model = Event
serializer_class = EventSerializer
paginate_by = 100
def pre_save(self, obj):
"""
Set the object's owner, based on the incoming request.
"""
obj.user = self.request.user
return super(EventListAPIView, self).pre_save(obj)
def post_save(self, obj, created=False):
print 'tags', self.request.DATA
obj.tags.add(self.request.DATA['tags'])
return super(EventListAPIView, self).post_save(obj)
But now as is said i have this error AttributeError: 'RelationsList' object has no attribute 'add'.
Actually, it's obvious since obj.tags is a list of object and not the TaggableManager anymore.
So i decided to start over and send my tags not in 'tags' but in another custom property 'tagged' to avoid conflit with the TaggableManager.
"TypeError: unhashable type: 'list'"
New error :) I found the solution with this django-taggit-unhashable-type-list
def post_save(self, obj, created=False):
map(obj.tags.add, self.request.DATA['tagged'])
return super(EventListAPIView, self).post_save(obj)
"TypeError: unhashable type: 'dict'"
Now, i figured it out that the tags i sent are not well formatted. I changed it (on the angularjs side) to send an array like this ['jazz','rock'] instead of [object, object]. Stupid mistake from a beginner.
Now the magic happen, response received by angularjs is good:
Sorry for my english. I know it may not be the best solution and i will try to update it when i'll find another solution.

Using a SELECT field for a OneToMany field

let's say I've the following very simple models:
class Customer(models.Model):
name = models.CharField(max_length=50)
class Probe(models.Model):
OwnerInfo = models.CharField(max_length=50)
comments = models.CharField(max_length=50)
customer = models.ForeignKey(Customer, null=True, blank=True)
I've been able to add an InLine to the Admin gui, but I'd like to use a SELECT component, so I can just select several Probes and assign them to the Customer. From this question:
one-to-many inline select with django admin
I know thanks to Luke's answer (last one) that I should create a custom Form and assign it to my ModelAdmin.form but I can not wonder how to tie it all together to make it work.
May anyone help?
Thanks a lot in advance.
OK, I came a step further, and now I've the field added to the Form, like this:
from django.contrib import admin
from django import forms
from web_gui.models import Probe, Customer, Firmware
class CustomerForm(forms.ModelForm):
probes = forms.ModelMultipleChoiceField(queryset=Probe.objects.all())
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['probes'].initial = [p.pk for p in Probe.objects.filter(customer_id=self.instance.pk)]
class Meta:
model = Customer
class CustomerAdmin(admin.ModelAdmin):
form = CustomerForm
admin.site.register(Probe)
admin.site.register(Customer, CustomerAdmin)
admin.site.register(Firmware)
but the initial values specified through "initial" are not being selected. What's wrong now? I assume that next will be to override the save() method to set the Probes on the Customer, am I right?
This is the best solution I've came up with. Let me know if there is any other better way of achieving this:
from django.contrib import admin
from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from web_gui.models import Probe, Customer, Firmware
class CustomerForm(forms.ModelForm):
probes = forms.ModelMultipleChoiceField(queryset = Probe.objects.all(), required=False)
probes.widget = FilteredSelectMultiple("Probes",False,attrs={'rows':'10'})
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['probes'].initial = [p.pk for p in Probe.objects.filter(customer_id=self.instance.pk)]
def save(self, force_insert=False, force_update=False, commit=True):
c = super(CustomerForm, self).save(commit=False)
c.probe_set = self.cleaned_data['probes']
c.save()
return c
class Meta:
model = Customer
class CustomerAdmin(admin.ModelAdmin):
form = CustomerForm
admin.site.register(Probe)
admin.site.register(Customer, CustomerAdmin)
admin.site.register(Firmware)

dynamic FilePathField question

I have a model where the location of pdf directory I'm pointing to with my FilePathField is based on the "client" and "job_number" fields.
class CCEntry(models.Model):
client = models.CharField(default="C_Comm", max_length=64)
job_number = models.CharField(max_length=30, unique=False, blank=False, null=False)
filename = models.CharField(max_length=64, unique=False, blank=True, null=True)
pdf = models.FilePathField(path="site_media/jobs/%s %s", match=".*\.pdf$", recursive=True
#property
def pdf(self):
return "site_media/jobs/%s %s" % (self.client, self.job_number)
def __unicode__ (self):
return u'%s %s' % (self.client, self.filename)
class Admin:
pass
I've tried to pass the client and job_number data to the pdf field dynamically by using a #property method on the model class, but either my approach or my syntax is fualty because the entire pdf field disappears in the admin. Any pointers on what I'm doing wrong?
Based on your subsequent post on similar functionality in the FileField (see last link below)
And my inability to get any of the above to work, I'm gonna hazard a guess that it's not yet possible for the FilePathField field type.
I know that passing a callable works for most fields' 'default' parameters...
https://docs.djangoproject.com/en/dev/ref/models/fields/#default
... as it appears to work for the upload_to param of FieldField
(eg https://stackoverflow.com/questions/10643889/dynamic-upload-field-arguments/ ) andImageField` (eg Django - passing extra arguments into upload_to callable function )
Anyone interested in extending FilePathField to include this feature?
Anyone interested in extending FilePathField to include this feature?
I'd love to see this extension!
Just for the record, this is the solution that worked for me (django 1.3):
# models.py
class Analysis(models.Model):
run = models.ForeignKey(SampleRun)
# Directory name depends on the foreign key
# (directory was created outside Django and gets filled by a script)
bam_file = models.FilePathField(max_length=500, blank=True, null=True)
# admin.py
class CustomAnalysisModelForm(forms.ModelForm):
class Meta:
model = Analysis
def __init__(self, *args, **kwargs):
super(CustomAnalysisModelForm, self).__init__(*args, **kwargs)
# This is an update
if self.instance.id:
# set dynamic path
mypath = settings.DATA_PATH + self.instance.run.sample.name
self.fields['bam_file'] = forms.FilePathField(path=mypath, match=".*bam$", recursive=True)
class AnalysisAdmin(admin.ModelAdmin):
form = CustomAnalysisModelForm
Hope this helps somebody out there.
try to set the path value as callable function
def get_path(instance, filename):
return "site_media/jobs/%s_%s/%s" % (instance.client, instance.job_number, filename)
class CCEntry(models.Model):
....
pdf = models.FilePathField(path=get_path, match=".*\.pdf$", recursive=True)
but I'm not sure if this works, I didn't test it.
Added an implementation of this based on Django v1.9 FilePathField implementation:
from django.db.models import FilePathField
class DynamicFilePathField(FilePathField):
def __init__(self, verbose_name=None, name=None, path='', match=None,
recursive=False, allow_files=True, allow_folders=False, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
if callable(self.path):
self.pathfunc, self.path = self.path, self.path()
self.allow_files, self.allow_folders = allow_files, allow_folders
kwargs['max_length'] = kwargs.get('max_length', 100)
super(FilePathField, self).__init__(verbose_name, name, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super(FilePathField, self).deconstruct()
if hasattr(self, "pathfunc"):
kwargs['path'] = self.pathfunc
return name, path, args, kwargs
And example use:
import os
from django.db import models
def get_data_path():
return os.path.abspath(os.path.join(os.path.dirname(__file__), 'data'))
class GenomeAssembly(models.Model):
name = models.CharField(
unique=True,
max_length=32)
chromosome_size_file = DynamicFilePathField(
unique=True,
max_length=128,
path=get_data_path,
recursive=False)