I have model:
class CategoryField(models.Model):
selectfield = models.IntegerField(choices=SELECT_FIELD_CHOICES, default=1)
verified = models.BooleanField(default=True, verbose_name='Required?')
In admin page I have selectField with choices: "value1", "value2", "value3", ...
When I select "value2" I need to show verified field. How can I do it?
You would need to add some JavaScript in order to show or hide the field. jQuery 1.9.1 is available in Django admin already via the django.jQuery object.
The simplest way to add this JavaScript is to add a Media meta class to your model form and add the form to the ModelAdmin:
# forms.py
from django import forms
class CategoryFieldForm(forms.ModelForm):
. . .
class Media:
js = ('category-field-admin.js',)
# admin.py
from django.contrib import admin
from your_app.forms import CategoryFieldForm
from your_app.models import CategoryField
class CategoryFieldAdmin(admin.ModelAdmin):
form = CategoryFieldForm
admin.site.register(CategoryField, CategoryFieldAdmin)
# category-field-admin.js
// pseudo code - change as needed
(function($) {
$(function() {
var selectField = $('#id_selectField'),
verified = $('#id_verified');
function toggleVerified(value) {
value == 'value2' ? verified.show() : verified.hide();
}
// show/hide on load based on pervious value of selectField
toggleVerified(selectField.val());
// show/hide on change
selectField.change(function() {
toggleVerified($(this).val());
});
});
})(django.jQuery);
Related
Imagine, that I have model and in the Django admin form I can update my fields. I want to implement something like: update one field and the second one will be reset in admin form in real time (I'll hope it can help real admin in the future do not forget redefine this second field, because it's very important in my situation). Is that possible to implement something like that without custom admin form?
To achieve this without a custom template or form, you can just include a custom script, for example:
Assuming you have an app named people and it has a model called Person with two fields first_name and last_name:
Include javascript into the admin page
# people/admin.py
from django.contrib import admin
from people.models import Person
class PersonAdmin(admin.ModelAdmin):
class Media:
js = ('people/js/person.js',)
admin.site.register(Person, PersonAdmin)
Then create the person/js/person.js script:
'use strict';
//execute when everything is loaded
window.addEventListener('load', () => {
let first_field = document.querySelector('form input[name=first_name]'); //field name
let second_field = document.querySelector('form input[name=last_name]'); //field name
//just when the fields are found (add or update)
if(first_field && second_field) {
//here you can implement whatever logic you want
first_field.addEventListener('input', () => {
second_field.value = '';
});
}
});
Now every time the first_name changes last_name will be cleared. This idea can be extended to do more interesting things.
I have a ModelForm with a FilteredSelectMultiple widget
from django import forms
from django.contrib import admin
class PortFolioForm(forms.ModelForm):
class Meta:
model = PortFolio
fields = ['title', 'description', 'keywords']
class Media:
css = {'all':['admin/css/widgets.css']}
js = ['/admin/jsi18n/']
keywords = forms.ModelMultipleChoiceField(label='Mots clés', widget=admin.widgets.FilteredSelectMultiple('Mots clés', False), queryset=Keyword.objects.all())
I'm using the form outside the admin, inside a view
c = {'form': PortFolioForm() }
return render(request, 'portfolio.html', c)
......
form = PortFolioForm(request.POST)
if form.is_valid():
form.save()
......
{{ form | crispy }}
When I'm already logged in as admin, the widget is dispayed as normal
If not it does not appear
I get the following js errors :
Refused to execute script from 'http://localhost/admin/login/?next=/admin/jsi18n/' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
Uncaught ReferenceError: interpolate is not defined
at Object.init (SelectFilter2.js:38)
at SelectFilter2.js:233
at NodeList.forEach ()
at SelectFilter2.js:231
As the website I'm working on is meant to be used by other users, I would like to use just the widget itself, because all the saving is done within the views, using form.save()
This could be because you are accessing a script under the admin url:
class Media:
css = {'all':['admin/css/widgets.css']}
js = ['/admin/jsi18n/']
You need to access the js outside the /admin/ location. Either copy the script to a folder not under admin/ and then change the location, or provide an url not under admin. For instance add to urls.py:
url(r'^jsi18n/$',
'django.views.i18n.javascript_catalog',
name='jsi18n')
And then change the form script url to just:
class Media:
css = {'all':['admin/css/widgets.css']}
js = ['jsi18n/']
Check Django's FilteredSelectMultiple widget only works when logged in
I'm currently trying to learn the integration of Django Rest Framework and React.js, the former for the backend and the latter for the frontend, by building a simple to-do application.
views.py
from rest_framework import viewsets
from . import models
from .serializers import ToDoSerializer, ToDoContainerSerializer
class ToDoContainerViewSet(viewsets.ModelViewSet):
queryset = models.ToDoContainer.objects.all().order_by('created')
serializer_class = ToDoContainerSerializer
serializers.py
from rest_framework.serializers import HyperlinkedModelSerializer
from . import models as todo_model
from rest_framework.serializers import ReadOnlyField
class ToDoContainerSerializer(HyperlinkedModelSerializer):
created_by = ReadOnlyField(source='created_by.id')
class Meta:
model = todo_model.ToDoContainer
fields = (
'url', 'id',
'created_by',
'todos_name',
'todos_important',
'todos_items_count',
)
extra_kwargs = {
'url': {
'view_name': 'todos:todocontainer-detail',
},
}
models.py
from django.db import models
from core.models import TimeStampedModel
from django.core.validators import MinValueValidator, MaxValueValidator
class ToDoContainer(TimeStampedModel):
created_by = models.ForeignKey(
user_model.User, on_delete=models.CASCADE, related_name="todo_container")
todos_name = models.CharField(max_length=50)
todos_important = models.BooleanField(default=False)
def todos_items_count(self):
todo_items = len(self.todo.all())
return int(todo_items)
def __str__(self):
return str(self.todos_name)
I built the views, serializers, models like above, and it seemed api generated properly like the below.
And I tried to get the json of the above to frontend by using the axios module like the below.
import React from 'react';
import axios from 'axios';
import ToDoCard from './ToDoCard';
class ToDoLists extends React.Component {
state = {
isLoading: true,
toDos: []
};
getToDos = async () => {
const { results } = await axios.get("/backend/todos-api/todo_container.json");
console.log(results) //Errors here, 'results' is undefined
this.setState({ toDos: results, isLoading: false })
}
componentDidMount() {
this.getToDos();
}
render() {
const { isLoading, toDos } = this.state;
return (<section className="container">
{isLoading ? (
<div className="loader">
<span className="loader__text">Loading...</span>
</div>
) : (
<div className="toDos">
{
toDos.map(toDo => {
return <ToDoCard
key={toDo.id}
id={toDo.id}
todos_name={toDo.todos_name}
todos_important={toDo.todos_important}
/>
})
}
</div>
)
}
</section>)
}
}
export default ToDoLists;
But the 'results' from 'axios.get("/backend/todos-api/todo_container.json");' was undefined, despite backend seemed fine like the below.
django result
[04/Jan/2021 20:38:07] "GET /backend/todos-api/todo_container.json HTTP/1.1" 200 372
I also set settings like the below and tried 'axios.get("/backend/todos-api/todo_container/");' but the result was the same. 'results' was undefined, and backend seemed fine.
settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer'
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
)
}
CORS_ORIGIN_WHITELIST = (
'https://localhost:3000',
'https://127.0.0.1:3000',
)
django result
[04/Jan/2021 20:32:13] "GET /backend/todos-api/todo_container/ HTTP/1.1" 200 364
What should I do to fix this?
I believe you should add append ?format=json instead of .json to the url. Unless you changed the url to be specifically ".json". But it's weird that you get a 200 response from both:
"/backend/todos-api/todo_container.json"
and
"/backend/todos-api/todo_container/"
can you post your urls.py?
Also try changing to, since you're unpacking the object, I think there's no field named results in the axios response, but there is data which is the actual json:
const { data } = await axios.get("/backend/todos-api/todo_container/");
I am trying to have a Django admin form field to be hidden until I select a specific value from a drop-down
I have tried everything including Jquery, the Jquery files load properly so my static root is pointing to the right file but when the admin site load and I change the values on the drop-down nothing happens.
I am using the latest Django and python 3.7 also I am using Django-jet as a customize admin template
models.py
class Incident(models.Model):
Incident_Type =models.ForeignKey(IncidentType,on_delete=models.DO_NOTHING,
null=True, blank=False)
DEM_REASON_CHOICES = (("Payments", "Payments"), ("Policies", "Policies"), ("Legal Issues", "Legal Issues"), ("Deactivation", "Deactivation"))
demonstration_reason = models.CharField(max_length=200, choices=DEM_REASON_CHOICES, null=True, blank=True)
admin.py
#admin.register(IncidentType)
class IncidentTypeAdmin(admin.ModelAdmin):
#admin.register(Incident)
class IncidentAdmin(admin.ModelAdmin):
form = IncidentAdminForm
forms.py
from django import forms
from .models import Incident
class IncidentAdminForm(forms.ModelForm):
class Meta:
model = Incident
widgets = {
'demonstration_reason': forms.SelectMultiple,
}
fields = "__all__"
class Media:
js = ('jet/showhide.js',)
My Jquery script
(function($) {
$(function() {
var selectField = $('#id_Incident_Type'),
verified = $('#id_demonstration_reason');
function toggleVerified(value) {
if (value === 'Demonstration') {
verified.show();
} else {
verified.hide();
}
}
// show/hide on load based on pervious value of selectField
toggleVerified(selectField.val());
// show/hide on change
selectField.change(function() {
toggleVerified($(this).val());
});
});
})(django.jQuery);
I am loading the script in my base.html like this
{% block scripts %}
<script src="{% static 'jet/showhide.js' %}"></script>
{% endblock %}
and when I run the server the js script load with the following message
"GET /static/jet/showhide.js HTTP/1.1" 304 0
I want demonstration_reason field ('#id_demonstration_reason') to be hidden until I select demonstaration from Incident_Type field ('#id_Incident_Type')
but with the current code when I go to the admin page and click on the model the demonstration_reason field is not hidden and nothing happens when I change the value of Incident_Type
I'd suggest select2. The library has a lot of other functionality, but chaining fields is what you're trying to do.
https://django-select2.readthedocs.io/en/latest/extra.html#chained-select2
How to get all the (unique) tags from django-taggit? I would like to display all the tags in a side bar.
Currently I am able to get all the tags for a particular post, but now I need to get all the unique tags in the entire blog.
code in models.py:
from django.db import models
from taggit.managers import TaggableManager
# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
created = models.DateTimeField()
tags = TaggableManager()
You can use all() to get all the tags in your database:
from taggit.models import Tag
tags = Tag.objects.all()
If you need a complete solution, have a look at django-taggit-templatetags. It provides several templatetags, including one for tag list, to expose various taggit APIs directly to templates.
The currently maintained fork supporting newer versions of django is:
https://github.com/fizista/django-taggit-templatetags2
django-taggit-templatetags has not been maintained for some years.
I know this is an old question...but I'm a Django newbie and found this question while looking for a way to fill an Ajax dropdown with all tag options. I figured out a way with djangorestframework and wanted to put a more complete solution here for others (OP would also be able to populate a sidebar with the response, or do anything else with it).
This adds an api endpoint tag so you can not only view them by navigating to /tag/ but get a JSON response suitable for Ajax (ergo, this assumes you have djangorestframework installed and in use).
serlializers.py
from taggit.models import Tag
class MyTagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['name', 'slug']
views.py
from taggit.models import Tag
class TagViewSet(viewsets.ModelViewSet):
"""
Not using taggit_serializer.serializers.TaggitSerializer because that's for listing
tags for an instance of a model
"""
queryset = Tag.objects.all().order_by('name')
serializer_class = MyTagSerializer
urls.py
router.register(r'tag', TagViewSet)
And, if you need the ajax:
$(document).ready(function(){
$.ajax({
url : "/tag/",
dataType: "json",
type: 'GET'
}).done(function(response){
for (var i in response) {
tagEntry = response[i];
$('#tagdropdown').append($('<option>', {
value: tagEntry.name,
text: tagEntry.name
}));
}
}).fail(function(response){
console.log('failed!');
console.log(response);
});
});