Hi I have setup a shopping cart in my app using this great article https://web-crunch.com/ruby-on-rails-ecommerce-music-shop/
My problem is that, as different items have different sellers, I need the items in the cart to be separated into different checkouts. I managed to do a group by based on the seller:
#groups = #cart.line_items.group_by {|line_item| line_item.listing.user_id}.values
but I am not sure how to get those separate groups into their own checkouts. Any help would be wonderful!
class LineItem < ApplicationRecord
belongs_to :listing
belongs_to :cart
def total_price
listing.price_cents.to_i * quantity.to_i
end
end
class Checkout < ApplicationRecord
belongs_to :user
has_many :line_items, dependent: :destroy
end
class Cart < ApplicationRecord
has_many :line_items, dependent: :destroy
def add_listing(listing)
current_item = line_items.find_by(listing_id: listing.id)
if current_item
current_item.increment(:quantity)
else
current_item = line_items.build(listing_id: listing.id)
end
current_item
end
def total_price
line_items.to_a.sum { |item| item.total_price }
end
end
Thank you
Related
I have a Rails app using Mongoid with the classes User, ContractAgreement and ContractVersions.
The relationships are the following: User embeds ContractAgreement, and ContractAgreement belong to ContractVersions, like so:
User:
class User
include Mongoid::Document
include Mongoid::Stateful
include Mongoid::Timestamps
[...]
embeds_many :contract_agreements
ContractAgreement:
class ContractAgreement
include Mongoid::Document
include Mongoid::Timestamps
embedded_in :user
belongs_to :contract_version
field :date_of_agreement, type: DateTime
ContractVersion
class ContractVersion
include Mongoid::Document
include Mongoid::Timestamps
field :version, type: String
field :description, type: String
field :status, type: String
has_one :post, autosave: true
has_one :post, autosave: true
#has_many :contract_agreements
As you can see, #has_many :contract_agreements is commented out as Mongoid didn't like the association to an embeded document.
Is there a way of getting the list of Users that have agreed to a specified contract?
I've tried both doing $elemMatch all the way:
[64] pry(main)> reload!;
u = User.all.select{|u| u.contract_agreements.count > 0}.first ;
cv = u.contract_agreements.first.contract_version ;
User.where(contract_agreements: {'$elemMatch' => {contract_version: {'$elemMatch' => {_id: cv._id}}}}).count
Reloading...
=> 0
As well as the compressed single query notation:
[64] pry(main)> reload!;
u = User.all.select{|u| u.contract_agreements.count > 0}.first ;
cv = u.contract_agreements.first.contract_version ;
User.where("contract_agreements.contract_version._id" => cv._id).count
Reloading...
=> 0
I know the contract version id I am asking for exists since I explicitly select a user that has one.
Is such a query even possible or do I need to de-embed ContractAgreement? If it is possible, what am I doing wrong?
Referring to the foreign key worked:
[73] pry(main)> reload!;
u = User.all.select{|u| u.contract_agreements.count > 0}.first ;
cv = u.contract_agreements.first.contract_version ;
User.where("contract_agreements.contract_version_id" => cv._id).count
Reloading...
=> 1
I'd like to Sum the post_value of all of the Posts for each post_user to eventually use in a chart. I'm struggling with how to formulate the query?
So far, I've got to:
user_totals = User.objects.annotate(post_value_total=Sum('post'))
models.py
class User(AbstractUser):
pass
class Post(models.Model):
post_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_cat = models.ForeignKey(Category, on_delete=models.CASCADE)
post_action = models.ForeignKey(Action, on_delete=models.CASCADE)
post_quantity = models.PositiveIntegerField(blank=True, null=True)
post_value = models.PositiveIntegerField(default='0')
post_timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.post_user}'s post at {self.post_timestamp}"
Thanks.
I'd like to Sum the post_value of all of the Posts for each post_user to eventually use in a chart.
Since each Post has a non-nullable post_user ForeignKey, it means that each Post belongs to exactly one user.
We thus can sum up the number of post_values of all Users with:
Post.objects.all().count()
If you only want to sum these up for a subset of the users, you can work with:
Post.objects.filter(
post_user__in=[user1, user2, user3]
).count()
or if you have ids:
Post.objects.filter(
post_user_id__in=[user_id1, user_id2, user_id3]
).count()
Or if you want to sum up the post_values, you can work with:
from django.db.models import Sum
total_post_value = Post.objects.aggregate(
total=Sum('post_value')
) or 0
The or 0 is necessary if the collection can be empty, since the sum of no records is NULL/None, not 0.
Or if you want to do this per User, we can work with:
user_totals = User.objects.annotate(
post_value_total=Sum('post__post_value')
)
The User objects that arise from this will have an extra attribute post_value_total that sums up the values of the related Posts. These can be None if a user has no related Posts. In that case we can work Coalesce [Django-doc]:
from django.db.models import Sum, Value
from django.db.models.functions import Coalesce
user_totals = User.objects.annotate(
post_value_total=Coalesce(Sum('post__post_value'), Value(0))
)
Well I'm have a PerformerSource and MonthlyEarning documents which has a field called performer_id
class PerformerSource
....
field :performer_id,:type => Integer
....
....
end
class MonthlyEarning
....
field :performer_id,:type => Integer
....
....
end
Now all I want to set a has_many relationships between the two documents with performer_id in mind i.e
performer_source has_many monthly_earnings
monthly_earning belongs_to performer_source
I think the following isn't allowed in Mongoid because apparently when I set the relationships
it just does not return anything
But if it does then please let me know
Apparently this work so the idea was to have relations through performer_id field present in both document so all that is need is
to set this
class PerformerSource
....
field :performer_id,:type => Integer
....
....
has_many :earnings ,:class_name => "MonthlyEarning",:primary_key => :performer_id,:foreign_key => :performer_id
end
class MonthlyEarning
....
field :performer_id,:type => Integer
....
....
belongs_to :performer,:class_name => "PerformerSource",:primary_key => :performer_id,:foreign_key => :performer_id
end
The way it works over here is the :primary_key i.e(performer_id) when firing the request through association
This is exactly what I want
I have 2 models:
Category(models.Model):
name = models.CharField(max_length=30)
no_of_posts = models.IntegerField(default=0) # a denormalised field to store post count
Post(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=100)
desc = models.TextField()
user = models.ForeignKey(User)
pub_date = models.DateTimeField(null=True, blank=True)
first_save = models.BooleanField()
Since I always want to show the no. of posts alongwith each category, I always count & store them every time a user creates or deletes a post this way:
## inside Post model ##
def save(self):
if not pub_date and first_save:
pub_date = datetime.datetime.now()
# counting & saving category posts when a post is 1st published
category = self.category
super(Post, self).save()
category.no_of_posts = Post.objects.filter(category=category).count()
category.save()
def delete(self):
category = self.category
super(Post, self).delete()
category.no_of_posts = Post.objects.filter(category=category).count()
category.save()
........
My question is whether, instead of counting every object, can we not use something like:
category.no_of_posts += 1 // in save() # and
category.no_of_posts -= 1 // in delete()
Or is there a better solution!
Oh, I missed that! I updated the post model to include the relationship!
Yes, a much better solution:
from django.db.models import Count
class CategoryManager(models.Manager):
def get_query_set(self, *args, **kwargs):
qs = super(CategoryManager, self).get_query_set(*args, **kwargs)
return qs.annotate(no_of_posts=Count('post'))
class Category(models.Model):
...
objects = CategoryManager()
Since you didn't show the relationship between Post and Category, I guessed on the Count('posts') part. You might have to fiddle with that.
Oh, and you'll want to get rid of the no_of_posts field from the model. It's not necessary with this. Or, you can just change the name of the annotation.
You'll still be able to get the post count with category.no_of_posts but you're making the database do the legwork for you.
i m looking for a way to manage multiple embedded objects in a form.
found a solution for formtastic by bowsersenior
Formtastic with Mongoid embedded_in relations
but i wasnt able to do the same for simple_form
formtastic:
= semantic_form_for #team do |form|
= #team.players.each do |player|
= form.inputs :for => [:players, player] do |player_form|
= player_form.input :name
best regards
sample
class Team
include Mongoid::Document
field :name, :type => String
embeds_many :players
end
class Player
include Mongoid::Document
embedded_in :team, :inverse_of => :players
field :name, :type => String
field :active, :type=> Boolean # checkboxes
end
Not sure if this would work but you might want to try something like this:
= simple_form_for #team do |form|
= f.input :name
= f.simple_fields_for #team.players do |player_form|
= player_form.input :name
Just keep in mind that you will have to create a new player in the team before the form will show up.
In your controller(controller):
def new
#team = Team.new
8.times { #team.players.new } #for 8 players
end