7

I have a django ecommerce project that works fine till I decided to improve it. I let users place order on certain services but every time they place an order they have to always input their details (name, emil, address etc) so I decided to upgrade the application so that user can add their billing address and it can be referenced in the order or continue as a guest.

Model

class Order(models.Model):
    first_name = models.CharField(max_length=50) 
    last_name = models.CharField(max_length=50)
    email = models.EmailField()
    address = models.CharField(max_length=250) 
    postal_code = models.CharField(max_length=20) 
    city = models.CharField(max_length=100)
    created = models.DateTimeField(auto_now_add=True) 
    updated = models.DateTimeField(auto_now=True) 
    paid = models.BooleanField(default=False)

class OrderItem(models.Model):
    order = models.ForeignKey(Order, related_name='items')
    product = models.ForeignKey(Product,related_name='order_items')
    price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.PositiveIntegerField(default=1)

view

def order_create(request):
    cart = Cart(request)
    if request.method == 'POST':
        form = OrderCreateForm(request.POST) 
        if form.is_valid():
            order = form.save() 
            for item in cart:
            OrderItem.objects.create(order=order, product=item['product'],price=item['price'], quantity=item['quantity'])
            cart.clear()
            return render(request,'order/created.html', {'order': order})
    else:
        form = OrderCreateForm()
    return render(request, 'order/create.html',{'cart': cart, 'form': form})

I then created an account app and extended django user so that user can register address and mobile so that the user does not have to type it every time he/she wants to place an order.

my new model is

class Order(models.Model):
    account = models.ForeignKey(Account, related_name='user_account')
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    paid = models.BooleanField(default=False)
    coupon = models.ForeignKey(Coupon, related_name='orders',null=True,blank=True)
    discount = models.IntegerField(default=0,validators=[MinValueValidator(0), MaxValueValidator(100)])

I am confused on how to code for my view. any assistance would help and I also have

def admin_order_detail(request, order_id):
    order = get_object_or_404(Order, id=order_id)
    #account = Account.objects.filter(request.user)
    context = {
        'order': order,
        }
    template = 'order/detail.html'
    return render(request,template,context)

to be able to view the order details in the admin area but I am only able to extend the FUll name, Email and phone number. Any help would be appreciated. Thanks in advance

A copy of all codes from github https://github.com/Olar19/ecommerce

King
  • 1,885
  • 3
  • 27
  • 84
  • Possible duplicate of [Extending the User model with custom fields in Django](https://stackoverflow.com/questions/44109/extending-the-user-model-with-custom-fields-in-django) – itzMEonTV Oct 28 '17 at 21:29
  • It is NOT a duplicate. – King Oct 28 '17 at 21:50

4 Answers4

1

This is a great idea to leverage the built in User object. Django is very "batteries included" meaning that it comes with much more out-of-the-box than say Flask and to me a large part of that the User auth system.

Normally you'll extend the User object with a one-to-one field which in your context looks like the Account object.

more detail on one-to-one fields (from the docs)

If you were B2B vice B2C then you may want the Account object to house the company name and then assign employees to that account where the Employee object would then be the extension from User.

So assuming the Account object looks something like:

class Account(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    ...

You could then access Account and User Objects from the Order Object.

order = get_object_or_404(Order, id=order_id)
username = order.user_account.username

or for permissions type stuff

orders = Order.objects.filter(user_account__user = request.user)
sahutchi
  • 2,223
  • 2
  • 19
  • 20
  • I dont think you can access `order.user_account` since `related_name` is useful for reverse relation.Also in `Order.objects.filter(user_account__user = request.user)` – itzMEonTV Oct 28 '17 at 21:26
  • I'm feeling dumb, so somebody help me out please. The order object has a downstream relation to the account object. And the account has a one-to-one downstream to user. So won't it work if instead of user_account you just use account... username = order.account.user.username. In other words I think it will work if you reference account with "account" vice "user_account". If it doesn't work I'll build a dummy app and see why it isn't working. Sorry for the busted solution. – sahutchi Oct 30 '17 at 15:38
1

What you are trying to do is something very common and reasonable to want to do. There are two main ways to add additional information to the Django User model. 1) You can override the django User model and define your own custom model or 2) you can create another model, say UserData, and have UserData have a OneToOneField connection with Django's User model. Thus, for every user, there is an associated UserData that stores the additional field information you need. It is difficult and not recommended to do option 1 in the middle of an existing project, so I would recommend transitioning to option 2.

The django documentation is very good on how to create a UserData model and link it to the User model. You can read about it here. A key note on this approach is that you need to a) go back and create a UserData object for all existing users (if any exist) and b) need to make sure to create a UserData object every time a user registers a user account on your website.

class UserData(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, default=0, related_name="userdata")
    # other fields ...

Now in your views, you can access the user data as such:

def admin_order_detail(request):
    ud = request.user.userdata

When you then want to access user data in a view, it is

I just looked over your code and it looks like you are not currently use Django's User system to have users register and login. I think a first step would be to read over the django documentation on the user auth system (here). Then, try and integrate user auth into your application. Finally, you can then create a custom userdata class to extend the user model.

Good luck!

Rohan Varma
  • 1,145
  • 6
  • 11
  • I am using Django-allauth for my authentication. Extending the user model isn’t the problem I’m facing. The problem is how to create a checkout system which would take the ordered items n values and check if the user has imputed a shipping address or user wants to create another shipping address in order to select any before proceeding to payments. – King Jan 20 '18 at 18:41
  • You can view my current codes from my github which I included the link in the question – King Jan 20 '18 at 18:46
0

you can use

order.account.user

if you want to join from order to user must use account (field name not related name)

if you want to join from account instance to orders can use this:

account.user_account.all()

related name is inverted access name. access from related object

so Its better to name related name of account field to orders

vorujack
  • 1,778
  • 16
  • 22
0

I think you should just create a Account model extending the AbstractUser with Email, Address as a OneToMany Field, to allow one user to have multiple addresses. Address can be a model with fields like

Name , Address Line 1 , Address Line 2 , postal_code, City , State , User(fk)

or a single text-field containing all-info and user-fk.

WhenEver a user chooses one of his address , you can just add address entry to Order-model for that corresponding order, this allows customer to select from one or more than one already provided address.

class Order(models.Model):
    account = models.ForeignKey(Account, related_name='user_account')
    address = models.ForeignKey(Address, related_name='address')
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    paid = models.BooleanField(default=False)
    coupon = models.ForeignKey(Coupon, related_name='orders',null=True,blank=True)
    discount = models.IntegerField(default=0,validators=[MinValueValidator(0), MaxValueValidator(100)])

This also enables us to retrieve both account-details and address for a given order via order.account and order.address .

Prateek099
  • 549
  • 1
  • 5
  • 15