django admin date-hierarchy on foreign key gives error - django-admin

In model.py
class Event(models.Model):
e_name=models.CharField(max_length=80)
e_desc=models.TextField(validators=[MaxLengthValidator(200)])
e_date=models.DateField()
u_id=models.ForeignKey(User) #owner of event
e_status=models.BooleanField()
e_participants=models.IntegerField()
e_date_of_req=models.DateField(auto_now_add=True)
e_answer_type=models.BooleanField()
e_criteria=models.CharField(max_length=80)
class UserEvent(models.Model):
u_id=models.ForeignKey(User)
e_id=models.ForeignKey(Event)
def __unicode__(self):
return "%s, %s" %(self.u_id,self.e_id)
in admin.py
class UserEventAdmin(admin.ModelAdmin):
list_display=(eventname,'e_date','e_status','user_name')
search_fields=('e_id__e_name','u_id__username','u_id__email',)
list_filter = ('e_id__e_status',)
date_hierarchy='e_date'
def e_date(self,obj):
return ("%s" % (obj.e_id.e_date))
e_date.short_description = 'Event Date'
def user_name(self,obj):
return obj.u_id.username
user_name.short_description = 'Owner'
def e_status(self,obj):
return obj.e_id.e_status
e_status.short_description = 'Event status'
e_status.boolean=True
admin.site.register(Event,UserEventAdmin)
I get error as:
UserEventAdmin.date_hierarchy' refers to field 'e_date' that is missing from model 'UserEvent'.

You cannot use a ModelAdmin method as a search field. It must be an attribute of your model.

Related

how to display the fields of related models in the admin panel through relationships model

I'm having trouble with the admin panel. The problem arose due to the presence of relationships model 'ElementVersion'. I don’t know how to display the fields of related models in the admin panel through ElementVersion.
My models.py file
class Refbook(models.Model):
code = models.CharField(max_length=100, unique=True, verbose_name='refbook code')
name = models.CharField(max_length=300, verbose_name='refbook name')
description = models.TextField()
def __str__(self):
return self.name
class Version(models.Model):
refbook_id = models.ForeignKey('Refbook',
on_delete=models.CASCADE,
related_name='versions',
verbose_name='refbook name')
version = models.CharField(max_length=50, verbose_name='refbook version')
date = models.DateField(verbose_name='start date')
def __str__(self):
return self.version
class Meta:
unique_together = [['refbook_id', 'version'], ['refbook_id', 'date']]
class Element(models.Model):
version_id = models.ManyToManyField('Version',
through='ElementVersion',
verbose_name='id_version')
code = models.CharField(max_length=100, unique=True, verbose_name='element's code')
value = models.CharField(max_length=300, verbose_name='element's value')
def __str__(self):
return self.code
class ElementVersion(models.Model):
version = models.ForeignKey(Version,
on_delete=models.CASCADE,
verbose_name='version')
element = models.ForeignKey(Element,
on_delete=models.CASCADE,
verbose_name='element')
class Meta:
unique_together = ('version', 'element')
This is the admin.py file
from .models import Refbook, Version, Element, ElementVersion
class VersionInline(admin.StackedInline):
model = Version
fields = ['date']
readonly_fields = ('date',)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
#admin.register(Refbook)
class RefbookAdmin(admin.ModelAdmin):
list_display = ['id', 'code', 'name', 'get_last_version', 'get_date_version']
inlines = [VersionInline]
def get_last_version(self, obj):
return obj.versions.values_list('version').last()
get_last_version.short_description = "last version"
def get_date_version(self, obj):
return obj.versions.values_list('date').last()
get_date_version.short_description = "start date"
class ElementVersionInline(admin.TabularInline):
model = ElementVersion
extra = 0
#admin.register(Version)
class VersionAdmin(admin.ModelAdmin):
list_display = ['get_code_refbook', 'refbook_id', 'version', 'date']
inlines = [ElementVersionInline]
def get_code_refbook(self, obj):
return obj.refbook_id.code
get_code_refbook.short_description = 'refbook code'
#admin.register(Element)
class ElementAdmin(admin.ModelAdmin):
list_display = ['id', 'code', 'value']
inlines = [ElementVersionInline]
I want to solve two problems:
on the version editing page, you need the ability to fill in the elements in this version. At the same time, I want all fields with readable names to be displayed, that is, the code and value
on the element editing page, you need the ability to select "reference-version" from the double list

How to convert string to existing attribute in model when creation

I got a array of strings, I want to retrieve for each the attribute during the creation of the post.
My array = ["_646_maturity", "_660_maturity", "_651_maturity", "_652_maturity", "_641_maturity"]
class Audit < ApplicationRecord
belongs_to :user
before_save :calculate_scoring
def calculate_scoring
scoring = []
models = ActiveRecord::Base.connection.tables.collect{|t| t.underscore.singularize.camelize.constantize rescue nil}
columns = models.collect{|m| m.column_names rescue nil}
columns[2].each do |c|
if c.include? "maturity"
Rails.logger.debug 'COLUMN : '+c.inspect
scoring.push(c)
end
end
getMaturity = ""
scoring.each do |e|
getMaturity = e.to_sym.inspect
Rails.logger.debug 'MATURITY : '+getMaturity
end
end
end
The log print > 'MATURITY : :_651_maturity'
I'm looking to the value of :_651_maturity who is a attribute of my post.
I tried .to_sym but it's not working..
Thanks for the help!
Inside calculate_scoring you can use self to point to the record you are saving. So self._651_maturity = <some_value>, self[:_651_maturity] = <some_value> and self['_651_maturity'] are all valid methods to set _651_maturity.
Also, you can do something like:
my_attrib = '_651_maturity'
self[my_attrib] = 'foo'

ruby on rails accessing custom class attributes from its object

I have a custom class in my application controller. Like below:
class Defaults
def initialize
#value_1 = "1234"
#value_2 = nil
#data = Data.new
end
end
class Data
def initialize
#data_1 = nil
end
end
Now in my controller method i have created an object of type Defaults
def updateDefaultValues
defaults = Defaults.new
# i am unable to update the value, it says undefined method
defaults.value_2 = Table.maximum("price")
defaults.data.data_1 = defaults.value_2 * 0.3
end
How to access value_2 from defaults object?
defaults.value_2
Also, how to access data_1 attribute from data object within defaults object?
defaults.data.data_1
You should use attr_accessor:
class Defaults
attr_accessor :value_1, :value_2, :data
# ...
end
defaults = Defaults.new
defaults.value_1 = 1
# => 1
defaults.value_1
# => 1
As you are using def as a keyword to define the method, that means def is a reserved keyword. You can't use reserved keywords as a variable.
You just need to rename your variable name from def to something_else and it should work! Your code will look like this:
def updateDefaultValues
obj = Defaults.new
obj.value_2 = Table.maximum("price")
obj.data.data_1
end
EDIT:
As per OP's comment & updated question, he had used def just as an example, here is the updated answer:
You may need attr_accessor to make attrs accessible:
class Defaults
attr_accessor :value_1, :value_2, :data
...
...
end
class Data
attr_accessor :data_1
...
...
end
Add value_2 method in Defaults class
class Defaults
def initialize
#value_1 = "1234"
#value_2 = nil
#data = Data.new
end
def value_2
#value_2
end
end
class Data
def initialize
#data_1 = nil
end
end

Virtual Column to count record

First, sorry for my English, I am totally new in ruby on rails even in very basic thing, so I hope you all can help me.
I have table Role and RoleUser
table Role have has_many relationship to RoleUser with role_id as foreign key
in table RoleUser is contain user_id, so I can call it 1 role have many users
and I want is to show all record in Role with additional field in every record called total_users,
total_users is in every record have role_id and count the user_id for every role, and put it in total_users,
I know this is must use the join table, but in rails I absolutely knew nothing about that, can you all give me a simple example how to do that.
and one more, same with case above, can I do for example Role.all and then the total_users in include in that without added it in database? is that use virtual column?
anyone have a good source of link to learn of that
I have following code in model
def with_filtering(params, holding_company_id)
order = []
if params[:sort].present?
JSON.parse(params[:sort]).each do |data|
order << "#{data['property']} #{data['direction']}"
end
end
order = 'id ASC' if order.blank?
if self.column_names.include? "holding_company_id"
string_conditions = ["holding_company_id = :holding_company_id"]
placeholder_conditions = { holding_company_id: holding_company_id.id }
else
string_conditions = []
placeholder_conditions = {}
end
if params[:filter].present?
JSON.parse(params[:filter]).each do |filter|
if filter['operation'] == 'between'
string_conditions << "#{filter['property']} >= :start_#{filter['property']} AND #{filter['property']} <= :end_#{filter['property']}"
placeholder_conditions["start_#{filter['property']}".to_sym] = filter['value1']
placeholder_conditions["end_#{filter['property']}".to_sym] = filter['value2']
elsif filter['operation'] == 'like'
string_conditions << "#{filter['property']} ilike :#{filter['property']}"
placeholder_conditions["#{filter['property']}".to_sym] = "%#{filter['value1']}%"
else
string_conditions << "#{filter['property']} = :#{filter['property']}"
placeholder_conditions["#{filter['property']}".to_sym] = filter['value1']
end
end
end
conditions = [string_conditions.join(' AND '), placeholder_conditions]
total_count = where(conditions).count
if params[:limit].blank? && params[:offset].blank?
data = where(conditions).order(order)
else
data = where(conditions).limit(params[:limit].to_i).offset(params[:offset].to_i).order(order)
end
return data, total_count.to_s
end
And I have follwing code in controllers
def crud_index(model)
data, total = Role.with_filtering(params, current_holding_company)
respond_to do |format|
format.json { render json: { data: data, total_count: total }.to_json, status: 200 }
end
end
My only purpose is to add virtual field called total_users, but i want added it in model and combine it with data in method with_filtering
If you have the models like this:
Class Role < ActiveRecord::Base
has_many :role_users
end
Class RoleUser < ActiveRecord::Base
belong_to :role
end
You could use select and joins to generate summary columns, but all the Role's attributes should be include in group.
roles = Role.select("roles.*, count(role_users.id) as total_users")
.joins(:role_users)
.group("roles.id")
Type those scripts in Rails console, Rails will generate a sql like :
SELECT roles.id, count(role_users.id) as total_users
FROM roles
INNER JOIN role_users
ON roles.id = role_users.role_id
GROUP BY roles.id
Then you can use roles.to_json to see the result. The summary column total_users can be accessed in every member of roles.
And there are many other way can match your requirement. Such as this. There is a reference of counter cache.
My suggestion is after searching, you can test those method by rails console, it's a useful tool.
UPDATE
According to OP's update and comment, seems you have more works to do.
STEP1: move with_filtering class method to controller
with_filtering handle a lot of parameter things to get conditions, it should be handled in controller instead of model. So we can transfer with_filtering into conditions and orders in controller.
class RolesController < ApplicationController
def conditions(params, holding_company_id)
if self.column_names.include? "holding_company_id"
string_conditions = ["holding_company_id = :holding_company_id"]
placeholder_conditions = { holding_company_id: holding_company_id.id }
else
string_conditions = []
placeholder_conditions = {}
end
if params[:filter].present?
JSON.parse(params[:filter]).each do |filter|
if filter['operation'] == 'between'
string_conditions << "#{filter['property']} >= :start_#{filter['property']} AND #{filter['property']} <= :end_#{filter['property']}"
placeholder_conditions["start_#{filter['property']}".to_sym] = filter['value1']
placeholder_conditions["end_#{filter['property']}".to_sym] = filter['value2']
elsif filter['operation'] == 'like'
string_conditions << "#{filter['property']} ilike :#{filter['property']}"
placeholder_conditions["#{filter['property']}".to_sym] = "%#{filter['value1']}%"
else
string_conditions << "#{filter['property']} = :#{filter['property']}"
placeholder_conditions["#{filter['property']}".to_sym] = filter['value1']
end
end
end
return [string_conditions.join(' AND '), placeholder_conditions]
end
def orders(params)
ord = []
if params[:sort].present?
JSON.parse(params[:sort]).each do |data|
ord << "#{data['property']} #{data['direction']}"
end
end
ord = 'id ASC' if ord.blank?
return ord
end
end
STEP2: update action crud_index with conditions and orders to get total_count of Roles.
class AnswersController < ApplicationController
def crud_index(model)
total = Role.where(conditions(params, current_holding_company)).count
if params[:limit].blank? && params[:offset].blank?
data = Role.where(conditions(params, current_holding_company)).order(orders(params))
else
data = Role.where(conditions(params, current_holding_company)).limit(params[:limit].to_i).offset(params[:offset].to_i).order(orders(params))
end
respond_to do |format|
format.json { render json: { data: data, total_count: total }.to_json, status: 200 }
end
end
end
STEP3: update action crud_index to get total_users by every role.
Make sure the two previous steps is pass the test.
class AnswersController < ApplicationController
def crud_index(model)
total = Role.where(conditions(params, current_holding_company)).count
if params[:limit].blank? && params[:offset].blank?
data =
Role.select(Role.column_names.map{|x| "Roles.#{x}"}.join(",") + " ,count(role_users.id) as total_users")
.joins(:role_users)
.group(Role.column_names.map{|x| "Roles.#{x}"}.join(","))
.where(conditions(params, current_holding_company))
.order(orders(params))
else
data =
Role.select(Role.column_names.map{|x| "Roles.#{x}"}.join(",") + " ,count(role_users.id) as total_users")
.joins(:role_users)
.group(Role.column_names.map{|x| "Roles.#{x}"}.join(","))
.where(conditions(params, current_holding_company))
.order(orders(params))
.limit(params[:limit].to_i)
.offset(params[:offset].to_i).order(orders(params))
end
respond_to do |format|
format.json { render json: { data: data, total_count: total }.to_json, status: 200 }
end
end
end
NOTE: step3 may need you to modify conditions and orders method to generate column_name with table_name prefix to avoid column name ambiguous error
If you can make these steps through, I suggest you can try will_paginate to simplify the part of your code about total_count ,limit and offset.
With what you explained, you could do something like this:
class Role < ActiveRecord::Base
has_many :role_users
has_many :users
def total_users
self.users.count
end
end
So you just need to call the total_users method on roles object which should get you what you desire. Something like this:
Role.first.total_users
# this will give you the total users for the first role found in your database.
Hope it helps
You might want to watch this Railscast too:
#app/models/role.rb
Class Role < ActiveRecord::Base
has_many :role_users
has_many :users, -> { select "users.*", "role_users.*", "count(role_users.user_id) as total_users" }, through: :role_users
end
This will allow you to call:
#roles = Role.find params[:id]
#roles.users.each do |role|
role.total_users
end
You can see more about how this works with a question I wrote some time ago - Using Delegate With has_many In Rails?
--
It's where I learnt about Alias columns, which Ryan Bates uses to count certain values:

Ruby on Rails classes and attributes initialization

I have this service class in RoR:
class PaypalService
attr_reader :api
def initialize()
#api = PayPal::SDK::Merchant::API.new
puts "PaypalService initilized"
end
# Paypal setExpressCheckout
def self.setExpressCheckout(billingType, returnURL, cancelURL, amount, description, isRecurring, locale)
new
if billingType == "credit-card"
billingType = "Billing"
else
billingType = "Login"
end
if isRecurring
varSetExpressCheckoutRequestDetails = createSetExpressCheckoutRecurringRequestDetails(billingType, returnURL, cancelURL, amount, description, isRecurring, locale)
else
varSetExpressCheckoutRequestDetails = createSetExpressCheckoutRequestDetails(billingType, returnURL, cancelURL, amount, description, isRecurring, locale)
end
#set_express_checkout = #api.build_set_express_checkout(varSetExpressCheckoutRequestDetails)
...
My call to the service is:
I want to initilize the #api attribute and use it whenever I execute one service method. This is my call from the client controller:
expressCheckoutResponse = PaypalService.setExpressCheckout(params[:paymentType],
"http://" + Yanpy::IP + "/app/index.html#/boat-booking-payment",
"http://" + Yanpy::IP + "/app/index.html#/boat-booking",
totalPrice,
description,
false,
'ES')
Log output:
PaypalService initilized
Completed 500 Internal Server Error in 625ms
NoMethodError (undefined method `build_set_express_checkout' for nil:NilClass):
lib/paypal_service.rb:30:in `setExpressCheckout'
app/controllers/bookings_controller.rb:275:in `create'
What am I missing?
def self.setExpressCheckout this is singleton method of the class PaypalService. Inside of which, #api is an instance variable, you are trying to access, is actually the instance varoable of the class PaypalService, not the instance variable of the instances PaypalService. As you didn't create any object of the class PaypalService, thus #api, which you are trying to aceess still not created, and eve-if you create it, you wouldn't be able to access it from the singleton methods of the class PaypalService, like you did.
Example :
class Foo
#x = 10
def self.class_instance_var
puts #x
end
def set_instance_var
#x = 12
end
def instance_var_of_instance
puts #x
end
end
foo = Foo.new
foo.set_instance_var
foo.instance_var_of_instance # => 12
Foo.class_instance_var # => 10
What you see, #x is an instance variable, of-course. But one is class Foos instance variable, and the other is instances of Foo, like foos instance variable. Name is same, bot those 2 #x has 2 different owners.

Resources