React on form changes - python

I have a general question to the django-admin.
Is it possible to react on form changes?
I have a select field in my django-admin detail site. Whenever I change the data from the select field, I want to change fields which are read-only.
Has anybody ever dealt with this issue?

My two cents:
As any other guys said it is a javascript work. In admin pages Django pases jquery. It is called django.jQuery. So basilly you would do what #Ashfaq suggested. You will create a custom_script.js and added to the Media metaclass.
Basically(as #Ashfaq):
class MyModelAdmin(admin.ModelAdmin):
class Media:
js = ("js/custom_script.js",)
and custom_script.js will be something like this(assuming that your select field is called id_category):
django.jQuery( document ).ready(function() {
console.log( "ready!" );
django.jQuery('#id_category').change(function() {
alert( "Handler for #id_category was called." );
});
});
The ready function will guarantee the handler is getting set.

I think the thing that will work here is to add jQuery + your custom javascript and play with the events / clicks what-ever you want with elements.
class MyModelAdmin(admin.ModelAdmin):
class Media:
js = ("js/custom_script.js",)
in custom_script you can add click or change events as you want.

Found a great answer by Abhijith K in another SO Q&A:
How can i add a onchange js event to Select widget in Django?
reciept=forms.ChoiceField(reciept_types, widget = forms.Select(attrs = {'onchange': "myFunction();"}))
To be clear, this is what you add within your widget definition: attrs = {'onchange': "myFunction();"} to define which JS function will be called, in this when an onchange event is triggers.
In the ModelAdmin you can then define a JavaScript file that you want to have access to, where you can define your function "myFunction()":
#admin.register(AnswerTree)
class AnswerTreeAdmin(ModelAdmin):
form = AnswerTreeForm
...
class Media:
js = ("admin_js/answertree_modeladmin.js",)
Django docs on defining assets (Like JavaScript & CSS) on your ModelAdmin:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-asset-definitions
What is also useful, I found out that you can access the value of the input by using attrs = {'onchange': "myFunction(this.value);"}, note that I am passing the argument this.value here now.
Example of the JavaScript function definition:
myFunction = (value) => {
console.log(value)
}
OR
myFunction(value) {
console.log(value)
}

Related

Converting like button to Ajax powered like button

I am working on a reverse blog project where users can like and comment.
Although I successfully implement the like button to work, But it refreshes the page.
My problem is I am trying to convert it to rather use Ajax to power the button.
And to as well uses a flag to decide which button to be shown, like or unlike.
Please I need help or the logic to guide me on converting the button to work on using Ajax
Please Note: I use pagination and the like button is on the list view and detail view (CBV).
Thank you in advance.
here is my view for the button
here is my model
here is my template rendering
Set a unique id for each like button if you have multiple like buttons. Bind the click event to JS function in which you will create an ajax call. Pass the ID of your model object (to increment likes count) to your JS function which will be passed in your ajax call to your view. Specify the URL of your view at which your like increment function resides.
Below is the implementation of ajax call on button click in django
https://stackoverflow.com/a/13733855/11979793
UPDATE: Class based view changes for the ajax
In your class-based view function:
Accept the parameters:
model_id = json.loads(request.POST.get('param_name')) # model_id could be your post ID to increase the likes
instead of render return JSON response for ajax:
return JsonResponse({
'like_count': model_to_dict(total_likes), # if you want to display total likes
'success': True
})
You can use total likes in success function of ajax to display it in your template.
P.S. import json at start of the file
UPDATE: example class-based view to handle ajax POST request
class myClassView(View):
template_name='index.html'
def post(self,request): # overriding the post method
myparam = json.loads(request.POST.get('paramFromAjax'))
# perform your actions here
# make a dictionary/context say mydic and pass parameters in that
return JsonResponse({
'paramToAjax': mydic,
'success': True
})

How to loop over custom plugin data on the template in django cms 3.5.3

I am trying to implement a website that uses RSS Feeds.
My use case is to be able to display one RSS feed at a time, then loop over to the next maybe after 5 seconds. These RSS feeds need to be displayed on the same placeholder.
I am new to django cms in case you need more info please let me know.
I have googled a lot but all I can see is how to add plugins from the frontend and they will all automatically show inside the placeholder.
Or modify the render method of the custom plugin class to display what you want.
But I want to display everything but one at a time in a continuous manner
#plugin_pool.register_plugin
class ExternalArticlePlugin(CMSPluginBase):
model = ExternalArticle
name = _("External article")
render_template = "external_article.html"
cache = False
def render(self, context, instance, placeholder):
context = super(ExternalArticlePlugin, self).render(
context, instance, placeholder
)
return context
I expect to display one RSS feed at a time inside my placeholder.
Those feeds are links to the actual webpage with more info.
one way would be to write a function random_rss_feed_url() in ExternalArticle Model which renders a random rss instance.
model.py
class ExternalArticle(models.Model):
def random_rss_feed_link(self):
return ExternalArticle.objects.order_by('?')[0].link
then you do in plugins external_article.html:
{{ instance.random_rss_feed_link }}
Edited:
if you want to change automatically without page reload, then you need something like this in javascript in your template:
var rss_links = ['link_1', 'link_2', 'link_3'];
setInterval(function() {
// take random rss link
var random_link = rss_links[Math.floor(Math.random()*rss_links.length)];
// change the link
document.getElementById('rss_link_element').href = random_link;
}, 5000);

Django autocomplete from json

I'm trying to add some autocomplete field in Django Admin (Django 2.0.1).
I managed to get it to work (both with the autocomplete included in Django and with Select2) because in those cases I loaded the dropdown options from a ForeignKey field.
Now I need to have autocomplete on a simple CharField but the choices need to be taken from a remote API that return a json response. I can decide how to structure the json response. Any way to do this?
The returned json responde doesn't represent objects of model, just simple text options.
Not sure if this fits your needs, but here was a solution to a similar problem (remote API, JSON, autocomplete text input). Select portions of the code:
HTML
<label>Which student? (search by last name.)</label>
<input type="text" name="studentname" id="student_name">
JS
// Build list of all students - hit API.
var ajax = new XMLHttpRequest();
ajax.open("GET", "example.com/api/student/?format=json", true);
ajax.onload = function() {
students = JSON.parse(ajax.responseText);
list = students.map(function(i) {
name = i.last_name + ', ' + i.first_name;
return name;
});
var input = document.getElementById("student_name");
new Awesomplete(input, { list: list });
};
ajax.send();
All this of course requires the Awesomplete JS library.
This is a solution when working outside the Django admin, but I think could be adapted to work within the admin setting without too much difficulty?
Perhaps something like this in your ModelAdmin?
def special_field(self, obj):
return render_to_string('special.html')
special_field.allow_tags = True
Then throw the aforementioned HTML/JS in special.html.
Finally you'll need to remove the old field from your ModelAdmin, add your new custom field, and likely override your ModelForm - something like this:
def save(self, commit=True):
extra_input = self.cleaned_data.get('studentname', None)
self.instance.full_name = extra_input # the model instance to save to
return super(NameOfYourForm, self).save(commit=commit)

Sailsjs route and template rendering

This question is directed to anyone with both flask (python) and sailsjs knowledge. I am very new to the concept of web frameworks and such. I started using flask but now I must use Sailsjs. In flask, I can define a route as:
#app.route('/company/<org_name>')
def myfunction(org_name):
...use org_name to filter my database and get data for that company...
return render_template('companies.html', org_name=org_name, mydata=mydata)
Where I can use myfunction() to render a template in which I can pass the parameters org_name and mydata.
In sails, I am confused as to how to define my route with a given parameter. I understand:
'/': {
view: 'companies'
}
but I am not sure how to make the route dynamic in order to accept any variable org_name.
Another problem is that in python, mydata is a query from a MySQL database. I have the same data base connected to Sailsjs with the model completely set up but I am sure this model is useless. The site that I am creating will not be producing any new data (i.e. I will neither be updating nor saving new data to the database).
My Question is thus: with
'/company/:org_name': {
view: 'companies'
}
where should I create the function that filters the database? how should I be sure that sails will pass that org_name parameter into the function and how should I pass the data as a parameter into an html template?
Thanks a ton.
There are 2 options here but it helps to explain a bit about each in order for you to pick the best course of action.
Firstly, routes...You can indeed as you have shown render a view from a route directly, but you can also do a few other things. Below are 2 snippets from the sails.js website docs:
module.exports.routes = {
'get /signup': { view: 'conversion/signup' },
'post /signup': 'AuthController.processSignup',
'get /login': { view: 'portal/login' },
'post /login': 'AuthController.processLogin',
'/logout': 'AuthController.logout',
'get /me': 'UserController.profile'
}
'get /privacy': {
view: 'users/privacy',
locals: {
layout: 'users'
}
},
Snippet 1 shows how you can render a view directly as you have shown but also how you can point to a controller in order to do some more complex logic.
The login within your controller could mean that for the /me "GET" route you can execute a database query within the "profile" method to accept a get parameter, find a user and then display a view with the users data within. An example of that would be:
Profile: function (req,res){
User.find({name: req.param('name')}.exec(function founduser(err,result){
return view('profile',{userdata: result});
});
}
In the second snipped from the sails docs you can see "locals" being mentioned. Here in the GET privacy route we see that the view is being told whether to use the layout template or not. However, with that being said there is nothing stopping you pushing more into the locals such as users name etc.
In my opinion and what I feel is best practice, I would leave your routes.js to be quite thin and logicless, put the database queries/logic/redirections in to your controller.
For your specific example:
My routes.js file may look like this
// config/routes.js
module.exports.routes = {
'get /companies': 'CompanyController.index',
'get /companies/:orgname': 'CompanyController.company'
}
This allows the first route to potentially show a list of companies by going to /companies and my second route may fire when a get request is made based on clicking a company name e.g. /companies/Siemens
My CompanyController.js for these may look like this:
module.exports = {
index: function (req, res) {
return res.view('companieslist');
},
company: function (req, res) {
var companyName = req.param('orgname'); //Get the company name supplied
//My company model which could be in api/models as Company.js is used to find the company
Company.find({name: companyName}).limit(1).exec(function companyresult(err,result){
//Error catch
if(err){return res.negotiate(err)};
//The result of our query is pushed into the view as a local
return res.view('company',{companydata: result}); //Within the .find callback to ensure we keep async
});
}
};
In my view I can access the data retrieved under "companydata" e.g. for EJS:
<%=companydata[0].name%>
If you need any further help/clarifications let me know. I do recommend taking a look at the sails.js documentation but if you really want to get your head around things I recommend sails.js in Action which is an ebook from mannings. A couple of days of reading really got me up to speed!

Customize Django Admin Advice

I have a series of filters I need to run and then I want the ability to loop though the results. I'm thinking of starting with a form where I can select the filter options. I want a next/previous buttons when I'm looping.
How would I implement this? I'm just looking for high level advice and sample code if available.
I know I can set index_template in AdminSite to create the first page. I know there is the SimpleListFilter but I don't think I can use it since I want multiple filters that need to be configured. Also I don't want to have to select all the models to loop though them. I plan on writing a custom add/change view.
I'm not sure how to go from the selected filter options to looping though each of the selected models. I'm not sure if I can pass and store a query set for when I loop though each model. Some of the options I've thought about is storing the filter parameters in the url and the current model number. Another thing I thought about is storing the results in database and recalling it.
Update
Someone thought this was too broad so I'll be a little more specific. I think the best solution will be to inherit from AdminSite and overwrite index_template to be a form that will contain the filters. How would I link the form submit to a view that will loop though the items? I assume I'll need to add a custom view to the admin but I'm not sure how to pass the data to the view.
This is quite a broad question but I'll give it a shot.
There are a few ways you can achieve this:
Setting up a model with filter queries as variables.
models:
class Filter(models.Model):
Filter_Query = models.CharField(max_length=30)
views:
from app_name.models import Filter, Some_Model
def filter(request, pk):
template = loader.get_template("app_name/filter_search.html")
filter_1 = Filter.objects.get(id=pk)
some_model = Some_Model.objects.all()
filter_1_search = model_name.filter(some_option=filter_1)
context = RequestContext(request, {'filter_1_search': filter_1_search})
return HttpResponse(template.render(context))
Then in a separate page, you can load the results like so.
{$("#some_div").load(filter/1)
or even easier you can you can just use AJAX to send whatever filter query you want.
views:
from app_name.models import Some_Model
def filter_query(request):
filter_1 = request.GET.get('filter_query', '')# Receives from AJAX
some_model = Some_Model.objects.all()
filter_1_search = model_name.filter(some_option=filter_1)
jsonDump = json.dumps(str(filter_1_search))
return HttpResponse(jsonDump, content_type='application/json')
javascript:
var data_JSON_Request = {'filter_query': filter_search1, 'csrfmiddlewaretoken': "{{csrf_token}}"};//JSON package.
function ajax_call(data_JSON_Request){
$(function jQuery_AJAX(){
$.ajax({
type: 'GET',
url: '/filter_query/',
data: data_JSON_Request,
datatype: "json",
success: function(data) {$("#sove_div").load(data);
open_model_menu();
},//success
error: function() {alert("failed...");}
});//.ajax
});//jQuery_AJAX
};//ajax_call

Categories

Resources