Django Admin Change List template - django-admin

I have this model:
class IllnessMedication(models.Model):
FREQUENCY_CHOICES = (
('D', 'Day'),
('W', 'Week'),
('M', 'Month'),
)
NUMERIC_CHOICES = (
(1, 1),
(2, 2),
(3, 3),
(4, 4),
)
illness = models.ForeignKey(Illness)
medication = models.ForeignKey(Medication)
strength = models.CharField("strength", max_length=25)
dosage = models.IntegerField("dosage", choices=NUMERIC_CHOICES)
class Meta:
db_table = "medication_illness"
class Medication(models.Model):
value = models.CharField("medication", max_length=50)
class Meta:
db_table = "medication"
class Illness(models.Model):
value = models.CharField("code", max_length=10)
description = models.CharField("description", max_length=100)
class Meta:
db_table = "illness"
I want to display the list view in group by fashion, i.e.
Illness1
Medication11
Strenght1 Dosage1
Strenght2 Dosage2
Strenght3 Dosage3
Medication12
Strenght1 Dosage1
Strenght2 Dosage2
Strenght3 Dosage3
Illness2
Medication21
Strenght1 Dosage1
..................
Is this possible to achieve by tweaking the django admin Model and ModelAdmin using available Django installed options/features/etc or I should expect to commit serious effort to overwrite the change_list.html template?
If the latter is true, please give me some pointers. Muchly appreciated.
Nghi

Related

GDALException OGR failure

I am working with Django DRF and GeoDjango for a simple model which is as follows.
class Company(models.Model):
name = models.CharField(max_length=200, default='Company', null=True)
def __unicode__(self):
return self.name
class Shop(models.Model):
name = models.CharField(max_length=200, default="bla")
address = models.CharField(max_length=300, default='blabla')
location = models.PointField(null=True, blank=True, geography=True)
company = models.ForeignKey(
Company, on_delete=models.CASCADE, null=True)
This is its serializer.py
class ShopSerializer(serializers.ModelSerializer):
distance = serializers.DecimalField(
source='distance.km', max_digits=10, decimal_places=2, required=False, read_only=True)
class Meta:
model = Shop
fields = ['id', 'name', 'address', 'location', 'distance']
# read_only_fields = ['distance']
class CompanySerializer(serializers.ModelSerializer):
shop_set = ShopSerializer(many=True)
class Meta:
model = Company
fields = ['id', 'name', 'shop_set']
def create(self, validated_data):
shop_validated_data = validated_data.pop('shop_set')
company = Company.objects.create(**validated_data)
shop_set_serializer = self.fields['shop_set']
for each in shop_validated_data:
each['company'] = company
shops = shop_set_serializer.create(shop_validated_data)
return company
everything works fine until I add rest_framework_gis in my settings.py file or add the following line in my shop serializer
serialize('geojson', Shop.objects.all(),
geometry_field='location', fields=('name', 'address'))
In both the cases get GDALException OGR failure.
I have checked my versions of GDAL and Python. Both are 64 bit. And both python and GDAL are working fine.
What I basically need to do here is to convert my POINT field into json lat long response right now the response is as such (if i do not include the lines that cause error).
{
"id": 1,
"name": "Cosmetica",
"address": "somwhere",
"location": "SRID=4326;POINT (24.896 67.182)"
}
Please help.

Django - Autocomplete Light in admin for join Model

So I have a join model defined as follow:
class EventTrack(models.Model):
dj = models.ForeignKey(DjProfile, blank=True)
track = models.ForeignKey(Track, blank=True)
event = models.ForeignKey(Event, blank=True)
def __str__(self):
return '%s - %s' % (self.event, self.track)
Is there a way I can use django-autocomplete-light with this model?
I know how to use it with inline models, but I don't get how to use it with standard field (in this case they are fk though).
At the moment I have the follow, which does not include the autocomplete functionality:
class EventTrackAdmin(admin.ModelAdmin):
fields = ['event', 'dj', 'track']
list_display = ('event', 'dj', 'track')
search_fields = ['event', 'dj', 'track']
admin.site.register(EventTrack, EventTrackAdmin)
edit:
I defined a DjForm as follow:
class DjForm(ModelForm):
dj = ModelChoiceField(
queryset=DjProfile.objects.all(),
widget=autocomplete.ModelSelect2(url='dj-autocomplete')
)
class Meta:
model = DjProfile
fields = '__all__'
Views:
#method_decorator(login_required, name='dispatch')
class DjProfileAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
# Don't forget to filter out results depending on the visitor !
if not self.request.user.is_authenticated():
return DjProfile.objects.none()
qs = DjProfile.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
And routing:
from frontend.views import DjProfileAutocomplete
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^dj-autocomplete/$', DjProfileAutocomplete.as_view(create_field='name'), name='dj-autocomplete'),
]
Everything works fine, and if I browse the endpoint I get the json with Djs result. My only problem is to use this in the EventTrack Model, in the admin.
sooo, actually it was easy:
forms.py
class EventTrackForm(ModelForm):
dj = ModelChoiceField(
queryset=DjProfile.objects.all(),
widget=autocomplete.ModelSelect2(url='dj-autocomplete')
)
class Meta:
model = EventTrack
fields = '__all__'
admin.py
class EventTrackAdmin(admin.ModelAdmin):
form = EventTrackForm
...

Clone current scope

Is it possible in Rails to get something like this:
scope = MyModel.filter_by({status: 1, color: 'red', user_id: 1})
count_status_2 = scope.clone.unscope(where: :status).where(status: 2)
count_status_3 = scope.clone.unscope(where: :status).where(status: 3)
I want to query all records by filter params and also get counts of the other results with almost the same filter, but different statuses. The example above doesn't work, it results in single query with 'status' = 3 in the SQL, so clone is not actually cloning here.
Thank you.
UPD.
I'd like to modify not only where but the whole scope.
Let's pretend we have list of some men:
men_scope = User.joins(:jobs, :city).where(sex: 0, age: 20).order(name: :desc)
and we also want to show women button next to that list, containing count of women of the same filter. I'd like to have something like
women_count = men_scope.uscope(where: :sex).where(sex: 1).count
but this does change the original men_scope so finally I get list of women instead of men.
You can do it this way:
params = {status: 1, color: 'red', user_id: 1}
a = MyModel.where(params)
b = MyModel.where(params.merge(status: 2))
c = MyModel.where(params.merge(status: 3))
or, to use one query do
requested_status = params.delete(:status)
x = MyModel.where(params).group(:status)
# And then use something like, not sure how hash will look
a = x[requested_status]
b = x['2']
c = x['3']
If you are using rails 4 you can make use of the rewhere and no need to save scopes
filter_1 = MyModel.where({status: 1, color: 'red', user_id: 1})
filter_2 = filter_1.rewhere(status: 2)
filter_3 = filter_1.rewhere(status: 3)

Field Validation in Admin when field are dependent on other fields

How can i apply validation in admin on various fields when they are dependent on each other ?
e.g. Let say in i have a Field A(BooleanField) and Field B (CharField) what i want to do is if in admin user select the Field A(checkbox) and does not enter anything in Field B
and if he tries to save ,it should throw an error like a normal blank=False gives. So how can i do this kind of validation in admin .
E.g Use Case
I have a table having the following structure :-
INTERVIEW_TYPES = (
('default', 'None'),
('Paired Visit','Paired Visit'),
('Time Series', 'Time Series'),
),
class Interview(models.Model):
ic_number = models.CharField(verbose_name ="Visit Configuration Number",max_length=20,unique=True,null =True,blank=True)
ic_description = models.TextField(verbose_name ="Visit Configuration Description",null = True,blank=True)
title = models.CharField(verbose_name ="Visit Configuration Title",max_length=80,unique=True)
starting_section = models.ForeignKey(Section)
interview_type = models.CharField(verbose_name = "Mapped Visit",choices=CHOICES.INTERVIEW_TYPES, max_length=80, default="Time Series")
select_rating = models.CharField(choices=CHOICES.QUESTION_RATING, max_length=80, default="Select Rating")
view_notes = models.CharField(choices=CHOICES.VIEW_NOTES, max_length=80, default="Display Notes")
revisit = models.BooleanField(default=False)
.....and so on ......
class Meta:
verbose_name = 'Visit Configuration'
verbose_name_plural = 'Visit Configurations'
# ordering = ('rpn_number',)
def __unicode__(self):
return self.title
Its admin.py
class InterviewAdmin(admin.ModelAdmin):
list_display = ('id','title', 'starting_section','ic_number','show_prior_responses')
raw_id_fields = ('starting_section',)
admin.site.register(Interview, InterviewAdmin)
In admin , If i select the checkbox of revisit and in the field interview_type(which will show a dropdown having choices None,Paired Visit , Time Series) if a User has selected None from that dropdown and then press save button it should throw me an error like a normal blank=False shows, saying "This field is required"
How can i do this kind validation where fields are dependent on each other ?
Please Ignore syntax error is any .
Thanks
I got confused in response_change and overriding clean method finally this is what i did
override clean method by making a model form in admin.py
class InterviewAdminForm(forms.ModelForm):
class Meta:
model = Interview
def clean(self, *args, **kwargs):
cleaned_data = super(InterviewAdminForm, self).clean(*args, **kwargs)
if self.cleaned_data['interview_type'] == "default" \
and self.cleaned_data['Revisit'] == True:
raise forms.ValidationError({'interview_type': ["error message",]})
return cleaned_data
class InterviewAdmin(admin.ModelAdmin):
# call the form for Validation
form = InterviewAdminForm
#....and so on ....

Django Admin: Many-to-Many listbox doesn't show up with a through parameter

I have the following models:
class Message(models.Model):
date = models.DateTimeField()
user = models.ForeignKey(User)
thread = models.ForeignKey('self', blank=True, null=True)
...
class Forum(models.Model):
name = models.CharField(max_length=24)
messages = models.ManyToManyField(Message, through="Message_forum", blank=True, null=True)
...
class Message_forum(models.Model):
message = models.ForeignKey(Message)
forum = models.ForeignKey(Forum)
status = models.IntegerField()
position = models.IntegerField(blank=True, null=True)
tags = models.ManyToManyField(Tag, blank=True, null=True)
In the admin site, when I go to add/change a forum, I don't see the messages listbox as you'd expect. However, it shows up if I remove the 'through' parameter in the ManyToManyField declaration. What's up with that? I've registered all three models (plus Tag) to the admin site in admin.py.
TIA
Documentation says:
When you specify an intermediary model using the through argument to a
ManyToManyField, the admin will not display a widget by default.
But it's probably possible to display M2M fields in the admin change view even if the through attribute is defined.
class ForumAdminForm(forms.ModelForm):
mm = forms.ModelMultipleChoiceField(
queryset=models.Message.objects.all(),
widget=FilteredSelectMultiple(_('ss'), False, attrs={'rows':'10'}))
def __init__(self, *args, **kwargs):
if 'instance' in kwargs:
initial = kwargs.setdefault('initial', {})
initial['mm'] = [t.service.pk for t in kwargs['instance'].message_forum_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self, commit)
old_save_m2m = self.save_m2m
def save_m2m():
old_save_m2m()
messages = [s for s in self.cleaned_data['ss']]
for mf in instance.message_forum_set.all():
if mf.service not in messages:
mf.delete()
else:
messages.remove(mf.service)
for message in messages:
Message_forum.objects.create(message=message, forum=instance)
self.save_m2m = save_m2m
return instance
class Meta:
model = models.Forum
class ForumAdmin(admin.ModelAdmin):
form = ForumAdminForm
Take a look at the official documentation:
I learned a lot from #Fedor's answer, but some comments and cleanup may be still beneficial.
class ForumAdminForm(forms.ModelForm):
messages = forms.ModelMultipleChoiceField(
queryset=Message.objects.all(),
widget=FilteredSelectMultiple('Message', False))
# Technically, you don't need to manually set initial here for ForumAdminForm
# However, you NEED to do the following for MessageAdminForm
def __init__(self, *args, **kwargs):
if 'instance' in kwargs:
# a record is being changed. building initial
initial = kwargs.setdefault('initial', {})
initial['messages'] = [t.message.pk for t in kwargs['instance'].message_forum_set.all()]
super(ForumAdminForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
if not self.is_valid():
raise HttpResponseForbidden
instance = super(ForumAdminForm, self).save(self, commit)
def save_m2m_with_through():
messages = [t for t in self.cleaned_data['messages']
old_memberships = instance.message_forum_set.all()
for old in old_memberships:
if old.message not in messages:
# and old membership is cleaned by the user
old.delete()
for message in [x for x in messages not in map(lambda x: x.message, old_memberships)]:
membership = Member_forum(message=messsage, forum=instance)
# You may have to initialize status, position and tag for your need
membership.save()
if commit:
save_m2m_with_through()
else:
self.save_m2m = save_m2m_with_through
return instance
class Meta:
model = Forum
fields = {'name', 'messages')
There's one caveat: if you have another many-to-many relationship in the models (that is without through), super(ForumAdminForm, self).save(self, commit) will set self.save_m2m in case commit is False. However, calling this would cause an error, because this function also tries to save the many-to-many with through as well. You may need to save all other many-to-many relationship manually, or catch the exception, or else.
Django admin nicely support many-to-many intermediary models that using the through argument .
For example you have these Person and Group models with intermediate Membership model:
models.py
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Now in admin.py file ,
Define an inline class for the intermediate Membership model:
#admin.register(Membership)
class MembershipInline(admin.TabularInline):
model = Membership
extra = 1
And use them in admin views of models:
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
#admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
inlines = (MembershipInline,)
More info in official docs:
Models,
Admin

Resources