django - how to override admin templates - django-admin

I want to change the way the add new object page looks in the admin page.
I do not want to change the fields but want to arrange the inputs, for example 2 fields on the firt line, then some paragraph then two other fields.
I read about add_form_template and my guess is that this is supposed to allow me to change the template without defining the form.
Here is my attempt which does not show the fields:
Does anyone know whether I need to define the form and pass it in and how?
If I have to pass in the form, then what is add_form_template used for?
from settings import BASE_DIR
import os
#admin.register(Owner)
class OwnerAdmin(admin.ModelAdmin):
add_form_template = os.path.join(BASE_DIR, 'dealer/templates/add_owner.html')
list_display = ('name', 'country', 'city', 'car')
#admin.register(Car)
class CarAdmin(admin.ModelAdmin):
list_display = ('name',)
//----- add_owner.html
{% extends "admin/base.html" %}
{% block content %}
<h1>New Owner</h1>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Create</button>
</form>
{% endblock %}

You don't have to change the admin templates to customize your field's layout. Use fieldsets instead:
# admin.py
#admin.register(Owner)
class OwnerAdmin(admin.ModelAdmin):
# ...
fieldsets = (
(None, {
'fields': (
('name', 'country'),
'city'
)
}),
('Another Fieldset', {
'description': 'Some information to display...',
'fields': (
'car',
),
}),
)

Related

Ruby/Rails button and search filtering

I've added the Active and Archived buttons to this page for extra filtering.
The existing search box functionality uses the following js.coffee which calls the controller index to pull the data from the db.
triggerSearch: (e) ->
if searchCompanyTimer
window.clearTimeout searchCompanyTimer
searchCompanyTimer = window.setTimeout(( ->
searchCompanyTimer = null
query_text = $(e.currentTarget).val()
el_id = $(e.currentTarget)[0].id
$.get( "companies", "q[name_cont]": query_text )
), 500, e)
I have added 2 similar js.coffee methods which set an active flag to true or false depending on which button was pressed.
Here is one of those methods.
triggerShowActive: (e) ->
if searchCompanyTimer
window.clearTimeout searchCompanyTimer
searchCompanyTimer = window.setTimeout(( ->
searchCompanyTimer = null
$.get( '/companies', {active: true} )
), 500, e)
here is part of my controller.
class CompaniesController < ApplicationController
respond_to :js, :json, :html
$active_inactive_flag = true
def index
puts "params are: #{params}"
puts "active param is: #{params[:active]}"
puts "#active_inactive_flag pre any conditions is: #{$active_inactive_flag}"
$active_inactive_flag = params[:active] ? params[:active] : $active_inactive_flag
puts "#active_inactive_flag after check on params[:active] is: #{$active_inactive_flag}"
if $active_inactive_flag.try :nonzero?
$active_inactive_flag = true
end
puts "#active_inactive_flag after check if it has a value, else true - is: #{$active_inactive_flag}"
#companies =
Company.search(params[:q])
.result
.order('created_at DESC')
.page(params[:page])
.per(Settings.paginate_limit)
.where(is_active: $active_inactive_flag)
end
here is my index.html.erb file where the buttons and search code is.
<div class="row">
<div class="col-md-3">
<label>Filter By:</label>
<button class="show_active btn btn-primary" style="border-radius:10px">Active</button>
<button class="show_inactive btn btn-primary" style="border-radius:10px">Archived </button>
</div>
<div class="col-md-9">
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">Filter by name</span>
<input class="form-control" id="q_name_cont" name="q[name_cont]" type="text">
</div>
</div>
</div>
I am using a global variable (tried instance and class variables also) in the controller but it's not working as expected. Sometimes the value in $active_inactive_flag switches from false to true or vice versa incorrectly. I don't want to use a global variable but I am at a loss as to how to combine both filter and search correctly. When the page loads we want Active button to be on by default and return active companies. The problem I am having is knowing what button was pressed when the search box is used.
Edit:
Here is an example of what is happening.
Any direction would be grateful.
Thanks Dave,
in js.coffee added the following:
returnActiveInctiveFlag = true
and then to the triggerSearch updated to:
$.get( "companies", { "q[name_cont]": query_text, active: returnActiveInctiveFlag } )
In my new methods updated returnActiveInctiveFlag accordingly.
Was really overcomplicating it. Thanks again for getting me thinking in the correct way.

Angular 5 - How to fill a FormArray

I will fill the customerNumberContainers which looks like this:
this.form = new FormGroup({
customerNumberContainers: new FormArray([
new FormGroup({
contactTenant: new FormControl('', [Validators.required, Validators.minLength(2)]),
customerNumber: new FormControl('', [Validators.required, Validators.minLength(2)])
}),
]),
Therefore I do this after I get the values over
this.contactService.findContactById(this.id).subscribe(response => { ...
Set values into form:
let customerNumberContainersFormArray: FormArray = this.form.controls.customerNumberContainers as FormArray;
customerNumberContainersFormArray.controls["0"].controls.contactTenant.value = 'TestValue';
but it is not shown with:
in Controller:
get customerNumberContainers(): FormArray {
return this.form.get("customerNumberContainers") as FormArray;
}
in Template:
<div formArrayName="customerNumberContainers">
<div *ngFor="let customerNumberContainer of customerNumberContainers.controls; index as i" [formGroupName]="i">
<mat-input-container class="full-width-input">
<input matInput formControlName="contactTenant">
</mat-input-container>
</div>
Does anyone known what I am doing wrong. It seems for me that values with *ngFor arn't refreshed.
why dont You just patch whole form with model ? like this:
set up your model, for example:
export class Tenants {
id: number;
customerNumberContainers: TenantContact[];
}
export class TenantContact {
contactTenant: string;
customerNumber: string;
}
fetch it from service like u always do but it should match above models and patch whole form (or setValue)
this.contactService.findContactById(this.id).subscribe((tenats: Tenants) => {
this.form.patchValue(tenats);
});

Setting default initial values for select multiple component

Using the select component i am having a hard time trying to set initial values.
As the docs. says im suppose to fill in the value prop with only string|number|string[]|number[].
Now the problem with this is that i need to show text on the input and send an id value on submit, but with this you show the same value you send.
<Select
mode="multiple"
defaultValue={tags} // => would need something like tags = [{id: 1, name: "Science"}]
placeholder="Select tags"
onSearch={this.fetchTags}
onChange={this.handleChange}
style={{ width: '100%' }}
>
{tags.map(tag => <Option value={tag.id} key={tag.id}>{tag.name}</Option>)}
</Select>
So "labelInValue" is what i really needed in case anyone else experience the same problem.
You can use value instead of defaulValue. Here is a sample code from my project:
const stateTasksOptions =
this.tasksStore.filters.init.state.map(item =>
<Select.Option key={item.id} value={item.id} title={<span className={`${item.id}Label`}>{item.title}</span>}>
<span className={`${item.id}Label`}>{item.title}</span> - <span class="normal-text">{item.help}</span>
</Select.Option>
)
return (
....
<Select
mode="multiple"
value={this.tasksStore.filters.selected.state.map(d => d)}
onChange={this.handleTasksStatus}
optionLabelProp="title"
>
{stateTasksOptions}
</Select>
....
)
And some css for colorizing.
Result:
this.tasksStore.filters.init.state looks like:
state = [
{id: "done", title: "Исполнена", help: "исполнены после 24.04.2017"},
{id: "active", title: "В работе", help: "текущие задачи"},
{id: "planned", title: "Запланирована", help: "задачи на будущее"},
{id: "missed", title: "Просрочена", help: "срок исполнения истек"},
{id: "archived", title: "Архив", help: "выполнены ранее 24.04.2017"}
]

How can I get Title & href (URL)?

I have following HTML code. I want to get the href & title of the product & store them into different variables. I have tried following code.
within("div.product-action") do
#product_url = find("a.href")
end
But that throws an error.
Capybara::ElementNotFound: Unable to find css "a.href"
My HTML code is as follow:
<div class="product-action zoom" ng-class="{ 'logged-out': !user.$isLoggedIn }">
<a href="/g/women/christian-dior/so-real-sunglasses-colorless" title="Christian Dior So Real" Sunglasses-Colorless" ng-click="ProductUtils.cache(result)" class="bottom-action-container quickview-button hover-option" track="{
type: 'product-info',
name: moduleType,
breadcrumbs: result.breadcrumbs || breadcrumbs
}">
<i class="icon-zoom"></i>
</a>
</div>
a.href will select the a elements that have a href class. This is not what you want.
You can access the attributes as a hash after you found the element:
a = find('.product-action a')
href = a[:href]
title = a[:title]
You can find the href and title of given html code with below mentioned code:
within("div.product-action") do
productUrl = find(:css, '.bottom-action-container')[:href]
productTitle = find(:css, '.bottom-action-container')[:title]
end
OR
within("div.product-action") do
productUrl = find('a')[:href]
productTitle = find('a')[:title]
end
Hope this helps :)

Ember direct URL or page refresh empty model

I have an index page with different courses. From that index page you can navigate to a specific course by a link-to. When I navigate to a course everything works fine but when I refresh the page or go to that URL directly the model is empty.
This is how my code looks like:
index.hbs ---------------------------------------
<div class="row">
<div class="col-md-6 col-md-offset-3 text-center">
<h1>Become a Tjuna Fish</h1>
<img src="http://placehold.it/500x300">
<p>Leer met de technieken werken die bij Tjuna worden gebruikt en ontwikkel jezelf tot een echte Tjuna Fish!</p>
</div>
</div>
<div class="row">
<h1 class="text-center">Cursussen</h1>
{{#each}}
<div class="col-md-4 text-center">
<div class="row">
<img {{bind-attr src="img"}}/>
</div>
<div class="row">
{{#link-to "course" this}}{{title}}{{/link-to}}
</div>
</div>
{{/each}}
</div>
scripts ---------------------------------------
BecomeTjunaFish.Router.map(function () {
// Add your routes here
this.resource('index', {path: '/'});
this.resource('course', { path: ':url'});
});
BecomeTjunaFish.IndexRoute = Ember.Route.extend({
// admittedly, this should be in IndexRoute and not in the
// top level ApplicationRoute; we're in transition... :-)
model: function () {
return this.store.find('course');
}
});
BecomeTjunaFish.CourseRoute = Ember.Route.extend({
// admittedly, this should be in IndexRoute and not in the
// top level ApplicationRoute; we're in transition... :-)
model: function (params) {
return this.store.find('course', params.id);
}
});
BecomeTjunaFish.Course = DS.Model.extend({
title: DS.attr('string'),
img: DS.attr('string'),
goal: DS.attr('string'),
targetGroup: DS.attr('string'),
prerequisites: DS.attr('string'),
url: DS.attr('string')
});
BecomeTjunaFish.Course.FIXTURES = [
{
id: 1,
title: 'Tjuna Basis',
img: 'http://placehold.it/200x200',
goal: 'kunnen werken met de basis tools en opmaaktalen die Tjuna gebruikt',
targetGroup: 'frontend developers in wording',
prerequisites: 'geen',
url: 'basis_cursus'
},
{
id: 2,
title: 'Tjuna Frontend',
img: 'http://placehold.it/200x200',
goal: '',
targetGroup: '',
prerequisites: '',
url: 'frontend_cursus'
},
{
id: 3,
title: 'Tjuna Backend',
img: 'http://placehold.it/200x200',
goal: '',
targetGroup: '',
prerequisites: '',
url: 'backend_cursus'
}
];
You need to specify the dynamic segment as :id in your router. What happens is,
When you transition via {{link-to}}, you pass the entire model object. Hence while retrieving the course model(this.store.find('course', params.id);) in route#model , you have the id with you and thereby fetching the model with no trouble.
When you hit back or refresh the course page, all you have is the course url in the address bar URL. This course url (note the entire course object) will be passed to the course route#model hook where you try to retrieve using the id. Hence it blows up
So make your dynamic segment as id in the router to make it work. You can also fetch the records with name.
Working Jsbin
As selvagsz explained, I had to change :url to :id in the router.
I also wanted to have nested URL's without nested templates. Something like this:
this.resource('index', {path: '/'});
this.resource('course', { path: ':course_id'}, function(){
this.resource('lesson', {path: ':lesson_id'}, function(){
this.resource('topic', {path: ':topic_id'});
});
});
Problem with this is, when I go to course/lesson url the lesson template will only render when I have an outlet in the course template. I want the course template to be replaced with the lesson template but keep the same nested url.
I fixed this by using the renderTemplate function of Ember like this:
BecomeTjunaFish.LessonRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('lesson', params.lesson_id);
},
renderTemplate: function() {
this.render('lesson', { into: 'application'})
}
});
This works great but when I navigate back, for example to course, it is not working anymore. Instead of only have a courseRoute I also needed a courseIndexRoute which uses the same model as courseRoute and place the renderTemplate in the CourseIndexRoute (same for LessonIndexRoute). Example:
BecomeTjunaFish.CourseRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('course', params.course_id);
}
});
BecomeTjunaFish.CourseIndexRoute = Ember.Route.extend({
model: function () {
return this.modelFor('course');
},
renderTemplate: function() {
this.render('course', { into: 'application'})
}
});
To me it seems to be a lot of code and I don't know if this is the right way to do this. At the moment this is good enough for me, it's working :) But I would appreciate it to have feedback on it and would like to know if there are other / better ways to fix this.
*I used this question as inspiration: Redirecting from edit to parent resource doesn't (re)render template

Resources