New to django, and loving it so far. I have a django admin, schema design question. I have a app that has a customer, product and order table. The customer has an order sheet of products, pre-built by admin, and saved in the CustomerProduct table. When the customer logs in they can only view their order sheet, update quantities, and that's it. They won't have access to products table or other customers order sheets. Three parts where im stuck right now,
The order table should have a many-to-many relationship with the customer_products table.
Order history needs to be saved in a separate table somehow, so if a user's order sheets changes, we still have record of their past orders. I need to construct a view for a user that will display just their order sheet. /admin/onlineordering/ordersheet.
How can I set this url up and limit access to a authenticated user. User A can only see User A's CustomerProduct (Order Sheet)
Below is the apps models.py and the admin.py
onlineordering/models.py
from django.conf import settings
from django.db import models
from datetime import datetime
class Customer(models.Model):
customer = models.ForeignKey(settings.AUTH_USER_MODEL, limit_choices_to={'groups__name': "customers"})
customer_product = models.ManyToManyField('Product', through='CustomerProduct')
company_name = models.CharField(max_length=255)
address1 = models.CharField(max_length=255)
address2 = models.CharField(max_length=255)
city = models.CharField(max_length=255)
state = models.CharField(max_length=255)
zip_code = models.CharField(max_length=255)
def __unicode__(self):
return self.company_name
class CustomerProduct(models.Model):
customer = models.ForeignKey('Customer')
product = models.ForeignKey('Product')
class Product(models.Model):
item = models.CharField(max_length=255)
description = models.CharField(max_length=255)
def __unicode__(self):
return self.description
class Order(models.Model):
customer_product_order = models.ManyToManyField('CustomerProduct', through='CustomerProductOrder')
purchase_order_number = models.CharField(max_length=10)
person_placing_order = models.CharField(max_length=255)
requested_delivery_date = models.DateField(blank=True, null=True)
class CustomerProductOrder(models.Model):
order = models.ForeignKey('Order')
customer_product = models.ForeignKey('CustomerProduct')
quantity = models.IntegerField(default=0)
onlineordering/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import Order,Customer,Product
UserAdmin.add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'password1', 'password2', 'first_name', 'last_name', 'groups')}
),
)
class CustomerProductInline(admin.StackedInline):
model = Customer.customer_product.through
extra = 0
class ProductAdmin(admin.ModelAdmin):
inlines = [
CustomerProductInline,
]
class CustomerAdmin(admin.ModelAdmin):
inlines = [
CustomerProductInline,
]
exclude = ('customer_product',)
admin.site.register(Order)
admin.site.register(Customer,CustomerAdmin)
admin.site.register(Product)
Update
/onlineordering/views.py
from django.http import HttpResponse
def ordersheet(request):
return HttpResponse("Hello, world.")
/urls.py
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/onlineordering/ordersheet', include(admin.site.urls)),
]
Related
I am pretty new to Django Rest Framework and am trying to build an API that has various viewsets, and almost all are working properly except one.
In my viewset I have 3 ModelViewSet's: one to list all Inspections, one to show all completed (done) inspections and the last one to show all the undone inspections. The problem is that it is returning the list of all inspections correctly, but the other 2 return "detail: not found" even though I have instances of Inspections in my database.
Here is my Inspection Viewset:
from rest_framework.viewsets import ModelViewSet
from inspections.models import Inspection
from .serializers import InspectionSerializer
from rest_framework import generics
class InspectionViewSet(ModelViewSet):
queryset = Inspection.objects.all()
serializer_class = InspectionSerializer
class DoneInspectionsViewSet(ModelViewSet):
serializer_class = InspectionSerializer
def get_queryset(self):
queryset = Inspection.objects.all()
return queryset
class UndoneInspectionsViewSet(ModelViewSet):
serializer_class = InspectionSerializer
def get_queryset(self):
queryset = Inspection.objects.filter(done=False)
return queryset
Here's my Inspection Serializer:
from rest_framework.serializers import ModelSerializer
from inspections.models import Inspection
from properties.api.serializers import PropertySerializer
class InspectionSerializer(ModelSerializer):
property = PropertySerializer(many=False)
class Meta:
model = Inspection
fields = ('id', 'connected_check', 'category', 'property', 'date',
'done_date', 'done_by', 'staff', 'notes', 'done')
Here's the Inspection model:
from django.db import models
from check.models import Check
from properties.models import Property
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils import timezone
class Inspection(models.Model):
connected_check = models.ForeignKey(Check, on_delete=models.DO_NOTHING, null=True, blank=True, related_name='inspection_check')
category = models.ForeignKey(InspectionCategory, on_delete=models.DO_NOTHING)
property = models.ForeignKey(Property, on_delete=models.DO_NOTHING, related_name='inspection_property')
date = models.DateField()
done_date = models.DateField(blank=True, null=True)
done_by = models.ForeignKey(User, max_length=25, blank=True, null=True, related_name='inspection_done_by', on_delete=models.DO_NOTHING)
staff = models.ForeignKey(User, max_length=25, null=True, blank=True, related_name='inspection_staff', on_delete=models.DO_NOTHING)
notes = models.TextField(max_length=500, blank=True)
done = models.BooleanField(default=False)
def __str__(self):
return self.category.name
def get_absolute_url(self):
return reverse("inspections:details",kwargs={'pk':self.pk})
def get_category_name(self):
return self.category
def is_past_due(self):
return timezone.now() > self.date
def is_done(self):
self.done = True
self.done_date = timezone.now()
self.save()
And here are the urlpatterns and router:
router = routers.DefaultRouter(trailing_slash=False)
router.register(r'inspection', InspectionViewSet)
router.register(r'inspection/done', DoneInspectionsViewSet, basename='done-inspection')
router.register(r'inspection/undone', UndoneInspectionsViewSet, basename='undone-inspection')
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^admin/', admin.site.urls),
url(r'^inspections/', include(('inspections.urls','inspections'), namespace='inspections')),
]
urlpatterns += [
path('api/', include(router.urls)),
path('api/schema/', get_schema_view(
title = "DAPIMS",
description = "API for DAPIMS",
version = "1.0.0"
), name='openapi-schema'),
]
I have already tried to change the queryset and noticed that even if I just want to return all Inspection objects it still returns "detail: not found" even though if I run the interactive shell and execute the same queryset it returns the correct objects.
Here's an example of the return from the interactive shell
And this is what my Browsable API is returning
Thanks in advance!
I can't figure out how to add the User full_name in the list_filter.
My Blog model is as follows:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils import timezone
class User(AbstractUser):
pass
class Post(models.Model):
POST_STATUS = (('borrador', 'Borrador'), ('publicado', 'Publicado'))
title = models.CharField('titulo', max_length=100)
body = models.TextField('cuerpo')
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts', verbose_name='autor')
created = models.DateTimeField('creado', auto_now_add=True)
published = models.DateTimeField('publicado', auto_now=True)
updated = models.DateTimeField('actualizado', auto_now=True)
slug = models.SlugField(max_length=100, unique_for_date='published')
status = models.CharField(max_length=10, choices=POST_STATUS, default='borrador')
class Meta:
ordering = ('-published',)
def __str__(self):
return self.title
As you can see, I have created a custom user model just in case I have to change it in the future.
In the Blog model, there is a 'author' field which uses the User as FK.
I want to add the posibility to filter by 'author' in the Blog's admin site. To do this I tried the following:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User, Post
admin.site.register(User, UserAdmin)
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'status', 'published')
list_filter = ('author' , 'status', 'published')
search_fields = [('title',), ('body',)]
prepopulated_fields = {'slug': ('title',)}
raw_id_fields = ('author',) # Reemplaza el drop-down para que parezca una lupa (para FKs)
date_hierarchy = 'published'
ordering = ('status', 'published')
The problem is that the filter by 'author' doesn't appear in the admin site:
If I do this: list_filter = ('author__last_name' , 'status', 'published') the filter shows up, but it looks like this ('appellido' means last_name in Spanish:
I would like the filter's text to be 'por autor (by author)' and be able to filter by the author's full name.
Is that possible?
Thanks in advance!!
Probably you can do that with django.contrib.admin.SimpleListFilter:
from datetime import date
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
class PerAuthorListFilter(admin.SimpleListFilter):
title = _('Per Author')
parameter_name = 'per_author'
def lookups(self, request, model_admin):
authors = []
for u in User.objects.all():
authors.append((u.pk, '{} {}'.format(u.first_name, u.last_name)))
return authors
def queryset(self, request, queryset):
queryset = super().queryset(request, queryset)
return queryset.filter(author=self.value())
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_filter = [PerAuthorListFilter, ...]
I am a Django beginner and I am trying to make read-only a 'price' field for an order. I think, based on what I have understood, this cannot be done inside the model itself, but rather inside a form.
Since I am using a CreateView generic view, I thought this could have been done by setting the attribute disabled equal to True, as said here.
so what I have done is, in views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .models import Order
from django import forms
# Create your views here.
class CreateOrderView(CreateView):
model = Order
template_name = 'home.html'
meal_price = forms.DecimalField(disabled=True)
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
But this doesn't work.
Here is my models.py
from django.db import models
from restaurant.models import Restaurant
from account.models import Customer
# Create your models here.
class Order(models.Model):
meal_name = models.CharField(max_length=255)
meal_price = models.DecimalField(max_digits=5, decimal_places=2)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, default=None)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, default=None)
Can anybody give me a hint?
Please consider that I am still learning so I would prefer coded answers to descriptive ones.
Thank you in advance
Ok, thanks to dirkgroten, I have worked out the answer.
Basically what is needed (in my case) is:
an Order model in models.py
from django.db import models
from restaurant.models import Restaurant
from account.models import Customer
# Create your models here.
class Order(models.Model):
meal_name = models.CharField(max_length=255)
meal_price = models.DecimalField(max_digits=5, decimal_places=2)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, default=None)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, default=None)
an OrderForm(ModelForm) in forms.py that modifies the price field setting the disabled attribute to true
from django.forms import ModelForm
from .models import Order
from django import forms
class OrderForm(ModelForm):
meal_price = forms.DecimalField(max_digits=5, decimal_places=2, disabled=True)
class Meta:
model = Order
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
an OrderView(CreateView) in views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .forms import OrderForm
# Create your views here.
class OrderView(CreateView):
form_class = OrderForm
template_name = 'home.html'
I have no experience with Django's CreateView but from what I read it works similar to a separate form. You could try something like this:
class CreateOrderView(CreateView):
model = Order
template_name = 'home.html'
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
def __init__(self, *args, **kwargs):
super(CreateOrderView, self).__init__(*args, **kwargs)
self.fields['meal_price'].widget.attrs['disabled'] = True
From my experience, the disabled attribute will be good for security reasons as far as protecting against the user editing the HTML and changing the value. However you won't be able to access this value when passed into a clean method. If you need to perform actions on the value you should change 'disabled' to 'readonly', but you won't have the same data protection that disabled offers.
I'm trying to model a rent contract in Django and use the admin form to insert and modify it.
Both owner and tenant can be companies (VAT number) or individuals (no VAT number). Companies and individuals are stored in two different models (Company and Individual).
I'm trying to solve this problem using Generic Foreign Key but I'm not able to show the tenant name in the admin page, only an integer field not friendly at all.
gestimm is the name of the app and that's my oversimplified models:
# my gestimm/models.py
#
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Individual(models.Model):
name = models.CharField(max_length=100, help_text='Name')
def __str__(self):
return self.name
class Company(models.Model):
name = models.CharField(max_length=100, help_text='Name')
def __str__(self):
return self.name
class Contract(models.Model):
description = models.CharField(max_length=30)
start = models.DateField()
stop = models.DateField()
def __str__(self):
return self.description
class Tenant(models.Model):
limit = models.Q(app_label='gestimm', model='individual') | models.Q(app_label='gestimm', model='company')
contract = models.ForeignKey(Contract, on_delete=models.CASCADE,
null=True, blank=True)
content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT,
help_text='Tenant', null=True,
limit_choices_to=limit)
object_id = models.PositiveIntegerField(null=True)
tenant = GenericForeignKey('content_type', 'object_id')
How I tried to solve the problem:
# my gestimm/admin.py
#
from django.contrib import admin
from .models import Individual, Company, Contract, Tenant
class TenantInline(admin.StackedInline):
model = Tenant
extra = 1
class ContractAdmin(admin.ModelAdmin):
inlines = [TenantInline]
admin.site.register(Individual)
admin.site.register(Company)
admin.site.register(Contract, ContractAdmin)
I found some old discussions but none of the proposed solutions worked.
Problem solved: I installed django-grappelli.
My new admin.py:
class TenantInline(admin.TabularInline):
model = Tenant
extra = 1
related_lookup_fields = {
'generic': [['content_type', 'object_id']],
}
class ContractAdmin(admin.ModelAdmin):
inlines = [
TenantInline,
]
admin.site.register(Contract, ContractAdmin)
As intended
My purpose is to see at the admin site only user name, email and phone number.
I've create UserProfile by extending User model:
model.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
name = models.CharField(max_length=50, null=True,blank=True)
address = models.CharField(max_length=50, null=True,blank=True)
phone = models.CharField(max_length=20, null=True,blank=True)
country = models.CharField(max_length=20, null=True,blank=True)
state = models.CharField(max_length=20, null=True,blank=True)
zip = models.CharField(max_length=10, null=True,blank=True)
code = models.CharField(max_length=40, null=True)
def user_email(self):
return self.user.email
admin.py
from myApp.models import UserProfile
from django.contrib import admin
class UserProfileAdmin(admin.ModelAdmin):
fields = ('name','phone',)
list_display = ('name','user_email',)
admin.site.register(UserProfile, UserProfileAdmin)
so on the list_display it works, I can see only the columns I've chosen, but when I add 'user_email' ( fields = ('name','user_email', 'phone',) )to fields I get when I try to go to admin site:
'UserProfileAdmin.fields' refers to field 'user_email' that is missing from the form.
Fields on a related model use two underscores. Dunno if it'll work in the admin though.
list_display = ('name','user__email',)
Just because I recently used it and you maybe want this, too: If you wan't to add an inline admin to the "User" admin page in Django you can do this (at least in Django 1.3) by doing:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from models import UserProfile
class UserProfileInlineAdmin(admin.StackedInline):
model = UserProfile
class MyUserAdmin(UserAdmin):
inlines = [ UserProfileInlineAdmin ]
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
You can't put editable fields from a related model into an admin form, without using inlines. You can show the field as a readonly value: just add it to readonly_fields.