How do you get the instance of the imported object after importing with django-import-export? - django-admin

I'm trying to add objects to a related model after importing using django-import-export.
My Models
class Event(models.Model):
title = models.CharField(max_length=150)
class EventSession(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="sessions")
start_date = models.DateTimeField()
end_date = models.DateTimeField()
My Import Data
id
start_date
start_time
minutes
sessions
9/1/21
10:00
60
2
9/8/21
10:00
60
3
My ModelResource Class
I am trying to override after_import_row to get the imported event and then create
the event sessions, but I don't know how to get the imported event.
class EventResource(resources.ModelResource):
start_date = fields.Field()
start_time = fields.Field()
def after_import_row(self, row, row_result, row_number=None, **kwargs):
""" After importing the row, get the saved event and save the event sessions."""
event = get_instance() # HOW TO GET THE INSTANCE????
start_datetime = timezone.make_aware(
datetime.combine(row["start_date"], row["start_time"])
)
end_datetime = start_datetime + timedelta(minutes=row["minutes"])
first_session = EventSession(
event=event,
start_date=start_datetime,
end_date=end_datetime,
)
first_session.save()
for _ in range(1, row["sessions"]):
# Sessions are always consecutive days
start_datetime = start_datetime + timedelta(days=1)
end_datetime = end_datetime + timedelta(days=1)
event_session = EventSession(
event=event,
start_date=start_datetime,
end_date=end_datetime,
)
event_session.save()
return super().after_import_row(
row, row_result, row_number=row_number, **kwargs
)
class Meta:
model = Event
fields = (
"id",
"title",
"start_date",
"start_time",
"sessions",
)
skip_unchanged = True
report_skipped = True

I figured out a solution. I can save the instance as an attribute of the EventResource object by overriding after_save_instance().
def after_save_instance(self, instance, using_transactions, dry_run):
self.instance = instance
return super().after_save_instance(instance, using_transactions, dry_run)
Then in after_import_row(), I use:
event = self.instance

You can also get hold of the instance id as follows:
def after_import_row(self, row, row_result, row_number=None, **kwargs):
print(row_result.object_id)
Obviously you can then load the object if required.
An alternative is to override after_import(). In this case, the result object passed to the method contains rows, which is a list of all imported rows.
def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
for row_result in result:
print(row_result.object_id)

Related

Adding objects to array on Lua

First of all I'm new on lua, I'm trying to implement a simple shopping cart as an object oriented exercise.
So I defined a Cart Object which stores several items objects
Cart = {
items = {},
discount = 0
}
function Cart:new(discount)
local object = {}
setmetatable(object, {__index = self})
self.discount = discount
return object
end
function Cart:addItem(item)
table.insert(self.items, item)
end
function Cart:GetTotal()
local total = 0
for i = 1, #self.items do
total = total + self.items[i]:GetPrice()
end
return total - self.discount
end
Each Item has the responsibility of calculate their price:
Item = {
units = 0,
pricePerUnit = 5,
name = ""
}
function Item:new(units, pricePerUnit, name)
local object = {}
setmetatable(object, {__index = self})
self.units = units
self.pricePerUnit = pricePerUnit
self.name = name
return object
end
function Item:GetPrice()
return self.units * self.pricePerUnit
end
But when I create the object and add items I get 60 as result, When I debugged the script I found that all the elements of the table are identical as if they were overwritten, could someone explain to me why and how can it be solved? Thanks in advance.
local shoppingCart = Cart:new(0)
shoppingCart:addItem(Item:new(1, 10, "Oranges"))
shoppingCart:addItem(Item:new(1, 15, "lemons"))
shoppingCart:addItem(Item:new(1, 20, "Strawberries"))
print(shoppingCart:GetTotal())
Cart:new and Item:new are meant to create new objects, therefore you call them on the classes themselves rather than on instances. That means the self that gets passed to those methods is those classes.
In both of those methods, you create an object table to become the new object, so you need to set the fields on that object, instead of modifying the class, eg, object.discount = discount.

Adding values to a hash within/over multiple each loops

I have a concept called snapshot which basically stores a snapshot of how data looked at a certain period of time. What I'm building is a method that loops through the snapshots for each events, and builds a small hash outlining the ownership over time for a given shareholder.
def fetch_ownership_over_time(shareholder, captable)
#shareholder = Shareholder.find(shareholder.id)
#captable = Captable.find(captable.id)
#company = #captable.company.id
#ownership_over_time = []
#captable.events.collect(&:snapshot).each do |snapshot|
parsed_snapshot = JSON.parse(snapshot)
#ownership_over_time.push(parsed_snapshot["event"]["name"])
#ownership_over_time.push(parsed_snapshot["event"]["date"])
parsed_snapshot["shareholders"].each do |shareholder|
if shareholder["id"] == #shareholder.id
#ownership_over_time.push(shareholder["ownership_percentage"])
end
end
end
return #ownership_over_time
end
I then call this method in my view which successfully retrieves the correct values however they are not structured in any way:
["Event 1 ", "2018-11-19", "0.666666666666667", "Event 2 ", "2018-11-19", "0.333333333333333", "4th event ", "2018-11-19", "0.315789473684211"]
What I'd like to do now though is construct my hash so that each separate snapshot event contains a name, date and ownership_percentage.
Perhaps something like this:
ownership_over_time = [
{
event_name = "Event 1" #parsed_snapshot["event"]["name"]
event_date = "20180202" #parsed_snapshot["event"]["date"]
ownership_percentage = 0.37 #shareholder["ownership_percentage"]
},
{
event_name = "Event 2" #parsed_snapshot["event"]["name"]
event_date = "20180501" #parsed_snapshot["event"]["date"]
ownership_percentage = 0.60 #shareholder["ownership_percentage"]
}
]
My challenge though is that the ["event"]["name"] an ["event"]["date"] attributes I need to fetch when looping over my snapshots i.e. the first loop (.each do |snapshot|) whereas I get my ownership_percentage when looping over shareholders - the second loop (.each do |shareholder|).
So my question is - how can I build this hash in "two" places so I can return the hash with the 3 attributes?
Appreciative of guidance/help - thank you!
You have to create a new hash for the object and append that hash to the array of objects you are creating.
def fetch_ownership_over_time(shareholder, captable)
#shareholder = Shareholder.find(shareholder.id)
#captable = Captable.find(captable.id)
#company = #captable.company.id
#ownership_over_time = []
#captable.events.collect(&:snapshot).each do |snapshot|
parsed_snapshot = JSON.parse(snapshot)
shareholder = parsed_snapshot['shareholders'].select { |s| s['id'] == #shareholder.id }.first
local_snapshot = {
'event_name' => parsed_snapshot['event']['name'],
'event_date' => parsed_snapshot['event']['date'],
'ownership_percentage' => shareholder.try(:[], "ownership_percentage") || 0
}
#ownership_over_time.push local_snapshot
end
return #ownership_over_time
end
Notice that I changed your second loop to a select. As you currently have it, you risk on pushing two percentages if the id is found twice.
EDIT:
Added functionality to use a default value if no shareholder is found.

Filterring ActiveRecord Relation...if there are no matches. (Null the active record relation)

I have a dashboard that allows for filtering of the results by different parameters. I build methods to filter the results by the given criteria. One area where I'm having trouble is if the previous line should null out the active record relation. Should I just put a bunch of if present? stat
def find_website_stats(options = {})
if options[:date_between].present?
start_date = options[:date_between].split(/-/).first.to_date
end_date = options[:date_between].split(/-/).last.to_date + 1
elsif options[:start_date].present?
start_date = options[:start_date].to_date
end_date = options[:end_date].to_date + 1 if options[:end_date].present?
end
contractor_name = options[:search_contractor] if options[:search_contractor].present?
distributor_name = options[:search_distributor] if options[:search_distributor].present?
distributor_ids = options[:with_distributor] if options[:with_distributor].present?
contractor_ids = options[:with_contractor] if options[:with_contractor].present?
with_country = options[:with_country] if options[:with_country].present?
with_state = options[:with_state] if options[:with_state].present?
search_city = options[:search_city] if options[:search_city].present?
web_stats = self.website_stats
if web_stats.present?
web_stats = web_stats.where(contractor_id: [*contractor_ids]) if contractor_ids.present?
if distributor_ids.present?
web_stat_ids = DistributorWebsiteStat.where(distributor_id: [*distributor_ids]).pluck(:website_stat_id)
web_stats = web_stats.where(id: [*web_stat_ids])
end
web_stats = web_stats.where(date_recorded: start_date..end_date) if start_date.present? && end_date.present?
web_stats = web_stats.with_country(with_country) if with_country.present?
web_stats = web_stats.with_state(with_state) if with_state.present?
web_stats = web_stats.search_city(search_city) if search_city.present?
#untested
if contractor_name.present?
searched_contractor_ids = Brand.search_contractor(contractor_name).pluck(:id)
web_stats = web_stats.where(contractor_id: [*searched_contractor_ids])
end
if distributor_name.present?
searched_distributor_ids = Brand.search_distributor(distributor_name).pluck(:id)
web_stat_ids = DistributorWebsiteStat.where(distributor_id: [*searched_distributor_ids])
web_stats = web_stats.where(id: [*web_stat_ids])
end
#end untested
end
web_stats
end
Where I'm specifically having a problem right now is the line that says if web_stat_ids.present?
So at first I grab all the website stats this object is associated with and then look to see if there are any for the given distributor.
If there is none for the given distributor web_stat_ids obviously returns nil
Then when I go to the line web_stats.where(id: [*web_stat_ids]) that's obviously going to return the same thing that I had before, rather than an empty active record relation, which is what I need it to be?
If I make this an empty array the next few statements with "where" won't work because it's an array and not an active record relation.
I know I can wrap this stuff in a bunch of if present? && statements...but I was wondering if there is a better solution to my problem?
In case anyone else is looking for this, found the answer from this SO post: How to return an empty ActiveRecord relation?
Model.none rails 4+

Call a variable from another model in Ruby on Rails

I'd like to ask a little question. It's rather trivial I guess but I might just look for the wrong solutions to my issue so I cant get it working.
I have a model called request.rb which has a method called self.dates
def self.dates
from_date = Request.last.date.to_date
to_date = Request.last.todate.to_date
weekdays = (from_date..to_date).select { |d| (1..5).include?(d.wday)}.count
weekend_days = (from_date..to_date).select { |d| [0, 6].include?(d.wday)}.count
end
I have another model called hotels.rb where I'd like to call the variables weekdays and weekend_days for the price calculation.
def self.best_deal_nm
weekday_nm_tot = (Request.dates.weekdays * pricea.wdpricenm) + (Request.dates.weekdays * pricea.wepricenm)
weekend_nm_tot = (Request.dates.weekend_days * priceb.wdpricenm) + (Request.dates.weekend_days * priceb.wepricenm)
[weekday_nm_tot, weekend_nm_tot].min
end
Unfortunately the code above doesnt work. My question is therefore how can I possibly call these two variables in my hotel model.
Thanks a lot for help
Just return all info in last line into your self.dates method like
def self.dates
from_date = Request.last.date.to_date
to_date = Request.last.todate.to_date
weekdays = (from_date..to_date).select { |d| (1..5).include?(d.wday)}.count
weekend_days = (from_date..to_date).select { |d| [0, 6].include?(d.wday)}.count
{'from_date' => from_date, 'to_date' => to_date, 'weekdays' => weekdays, 'weekend_days' => weekend_days}
end
After call Request.dates from hotels.rb you could access to all variables added to hash.
weekday_nm_tot = (Request.dates['weekdays'] * pricea.wdpricenm) + (Request.dates['weekdays'] * pricea.wepricenm)

How to "shift" objects from ActiveRecord array

I have this method
def gen_events(score)
events = location.events
(1..rand(5..7)).each do |n|
random = rand(0.139..1).round(3)
rarity = Character.get_rarity(random)
event = events.where(rarity: rarity).shuffle.shift #<-- HERE
self.events << event
end
end
Currently, the shift method only grabs the first element, but doesn't remove it, how can I go about making it so that it does both?
This is not an array: events.where(rarity: rarity), this is an ActiveRecord scope, you can't remove things from it without destroying and erasing them from database. Instead, you should keep an array of object you already found, and use it to filter future results:
def gen_events(score)
events = location.events
new_events = []
(1..rand(5..7)).each do |n|
random = rand(0.139..1).round(3)
rarity = Character.get_rarity(random)
event = events.where(rarity: rarity).where.not(id: new_events.map(&:id).sample
new_events << event
end
self.events << new_events
end

Resources