thank you for reading, i have problem in Django Rest Framework project, i wrote only backend, and i must return url with user info from fields:
models.py
class Payment(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT) # we cannot delete user with money
# em
email = models.CharField(max_length=255, blank=False, null=True)
# m
shop_id = models.CharField(max_length=30, blank=False, null=True)
# oa
amount = models.DecimalField(default=0, max_digits=12, decimal_places=2)
class Curency(models.TextChoices):
UAH = 'UAH'
USD = 'USD'
EUR = 'EUR'
KZT = 'KZT'
RUB = 'RUB'
# currency
currency = models.CharField(
max_length=10,
choices=Curency.choices,
default=Curency.USD
)
# o
order_id = models.CharField(
max_length=255, null=True, blank=False, unique=False)
# s
sign = models.CharField(max_length=255, blank=False, null=True)
url = models.CharField(max_length=1000, null=True)
def __str__(self):
return f'Payment {self.user} of game {self.order_id}'
serializers.py
class PaymentSerializer(serializers.ModelSerializer):
# in order to work you should add related_name in Action model
class Meta:
model = Payment
fields = '__all__'
# balance is read only, because i don't want someone create account
# with money
read_only_fields = ('id', 'shop_id', 'sign', 'user', 'email')
views.py
class PaymentViewSet(viewsets.ModelViewSet,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated, )
serializer_class = PaymentSerializer
queryset = Payment.objects.all()
def get_queryset(self):
"""Return object for current authenticated user only"""
return self.queryset.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
serializer.save(email=self.request.user.email)
shop_id = 'some id'
secret1 = "secret1"
amount = Payment.objects.get(amount) #there is problem
currency = Payment.objects.get(currency) #there is problem
order_id = Payment.objects.get(order_id) #there is problem
list_for_sign = map(
str, [shop_id, amount, secret1, currency, order_id])
result_string = ":".join(list_for_sign).encode()
sign_hash = hashlib.md5(result_string)
sign = sign_hash.hexdigest()
serializer.save(shop_id=shop_id)
serializer.save(sign=sign)
serializer.save(url="https://pay.freekassa.ru/?m={0}&oa={1}&o={2}&s={3}&em={4}¤cy={5}".format(
shop_id, amount, order_id, sign, self.request.user.email, currency))
return sign
i wanna put info when i post it in form, but have problem:
UnboundLocalError at /pay/payments/
local variable 'amount' referenced before assignment
i understand, that i take order, amount and currency before i give it, but what i should do?)
Update, form in DRF looks like:
DRF form looks like
You can simply access the form data like so:
amount = serializer.validated_data['amount']
or
amount = self.request.data["amount"]
Then you can do whatever you want with those variables, like to use it to search in the database
Related
I am trying out an implementation. Although I have figured the logic out, I am having issues representing it programatically. I need your help or guidance.
Below is the detailed explanation of my code and what i am trying to achieve, please pardon me as it would be a long read.
What I want to achieve
I want to create an endpoint - /order/tradeadvisor/{{producer_order_id}}, so that if the endpoint is hit, then it should first record the producer_order_id in a variable, then it should go to the Order model and loop through it fetching all order_id, user_id with the user_type=1.
After that it should, now produce a a single record where, the user caprice is equal to producer floorprice(where the producer is the logged in user) and then the user needed engery is equal to the producers surplus and then store this record in the Trade table.
What i have done
User.model:
class User(AbstractBaseUser, PermissionsMixin):
dso = models.ForeignKey(to=Dso,related_name='dso',null=True,on_delete=models.CASCADE)
name = models.CharField(max_length=70)
address = models.CharField(max_length=70)
roleId = models.IntegerField(default=1)
customerId = models.CharField(max_length=70, blank=False, default='')
floorPrice = models.DecimalField(max_digits=10, max_length=255, decimal_places=2, null=True)
capPrice = models.DecimalField(max_digits=10, max_length=255, decimal_places=2, null=True)
tradeStrategy = models.CharField(max_length=255, null=True)
username=models.CharField(max_length=255, unique=True, db_index=True)
email=models.EmailField(max_length=255, unique=True, db_index=True)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_trading = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
created_at=models.DateTimeField(auto_now_add=True)
updated_at=models.DateTimeField(auto_now=True)
User serializer:
class UserSerializer(serializers.ModelSerializer):
energy_data = EnergyDataSerializer(read_only=True)
dso = DsoSerializer(read_only = True)
class Meta:
model = User
fields = ('id',
'name',
'email',
'address',
'roleId',
'is_active',
'customerId',
'dso',
'floorPrice',
'capPrice',
'tradeStrategy',
'username',
'is_verified',
'is_staff',
'is_trading',
'created_at',
'updated_at',
'energy_data', //this is a nested dictionary holding data of the energySurplus and energyNeeded
)
depth = 1
Trade Serializer:
class TradeSerializer(serializers.ModelSerializer):
consumer_id = serializers.PrimaryKeyRelatedField(allow_null=False, queryset=User.objects.all())
producer_id = serializers.PrimaryKeyRelatedField(allow_null=False, queryset=User.objects.all())
c_order_id = serializers.PrimaryKeyRelatedField(allow_null=False, queryset=Order.objects.all())
p_order_id = serializers.PrimaryKeyRelatedField(allow_null=False, queryset=Order.objects.all())
startTime = serializers.DateTimeField()
class Meta:
model = Trade
fields = ('id',
'startTime',
'stopTime',
'price',
'c_order_id',
'p_order_id',
'consumer_id',
'producer_id',
'producer_location',
'consumer_location',
'energyQuantity',
)
Order Serializer
class OrderSerializer(serializers.ModelSerializer):
trades = TradeSerializer(read_only=True, many= True)
user_id = serializers.PrimaryKeyRelatedField(allow_null=False, queryset=User.objects.all())
user_type = serializers.IntegerField()
created_at = serializers.DateTimeField()
class Meta:
model = Order
fields = ('id',
'user_id',
'user_type',
'trades',
'created_at',
)
depth = 1
Views.py:
class TradeAdvisor(views.APIView):
serializer_class = TradeSerializer
permission_classes = (permissions.IsAuthenticated,)
def get(self, request, *args, **kwargs):
user = self.request.user
producer_order_id = self.kwargs['p_order_id']
orders = Order.objects.filter(user_type=1)
for order in orders:
consumer = order.user_id
if consumer['user_id']['is_trading']:
if ((consumer['capPrice'] == user.floorPrice ) and (consumer['energy_data']['energyNeeded'] == user.energy_data['energySurplus'])):
date_time = datetime.datetime.now()
data ={
"startTime": date_time,
"stopTime": "",
"price": user.flooPrice,
"c_order_id": order.id,
"p_order_id": producer_order_id,
"consumer_id": consumer,
"producer_id": user,
"producer_location": user.address,
"consumer_location": consumer['address'],
"energyQuantity": user.energy_data['energySurplus']
}
serializer = self.serializer_class(data=data)
serializer.save()
else:
return Response({'error': 'No active consumers'}, status = status.HTTP_400_BAD_REQUEST)
else:
return Response({'error': 'No active consumers'}, status = status.HTTP_400_BAD_REQUEST)
So this is what i have tried, i am pretty sure is wrong and also i do get an error
'int'(consumer['user_id']['is_trading']) object is not subscriptable
Just use consumer as a User instance, i.e:
if consumer.is_trading:
# instead of
if consumer['user_id']['is_trading']
and later you want to check Order, not User. It's not about Django knowledge, everything here is almost pure pythonish.
PS. please don't set ForeignKey field with _id. It's very misleading for other developers. I've just lost 5 minutes because I didn't realise that.
is it possible to add an age field that is auto filled in the runtime based on another date of birth field at the django admin interface, i added a screenshot trying to explain more what i mean
my models.py
class FamilyMember(models.Model):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
family_group = models.ForeignKey(FamilyGroup,
on_delete=models.CASCADE,
null=True,
blank=True)
name = models.CharField(max_length=100, null=True, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
relationship = models.ForeignKey(Relationship, on_delete=models.PROTECT)
dependant_child_age_range = models.ForeignKey(DependantChildAgeRange,
null=True,
blank=True,
on_delete=models.PROTECT)
care_percentage = models.PositiveSmallIntegerField(
null=True, blank=True, validators=[
MaxValueValidator(100),
])
income = models.DecimalField(max_digits=6,
decimal_places=2,
null=True,
blank=True)
rent_percentage = models.PositiveSmallIntegerField(
null=True, blank=True, validators=[
MaxValueValidator(100),
])
admin.py
class FamilyMemberInline(admin.TabularInline):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
action = request.META['PATH_INFO'].strip('/').split('/')[-1]
if action == 'change':
transaction_id = request.META['PATH_INFO'].strip('/').split('/')[-2]
if db_field.name == "family_group":
kwargs["queryset"] = FamilyGroup.objects.filter(transaction=transaction_id)
return super(FamilyMemberInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
model = FamilyMember
extra = 0
def sufficient_info_provided (self, obj):
return obj.sufficient_information_provided
sufficient_info_provided.boolean = True
readonly_fields = ['sufficient_info_provided',]
Override your inline's get_queryset method to annotate the queryset with the calculation. The annotation will add an age attribute to each object in the queryset.
Then as you can see in the ModelAdmin.list_display documentation, you can include a string representing a ModelAdmin method that accepts one argument, the model instance. Inline's work in the same way but you must include the method in ModelAdmin.readonly_fields.
Putting it all together:
class FamilyMemberInline(admin.TabularInline):
...
fields = (..., 'get_age')
readonly_fields = ('get_age',)
def get_queryset(self, request):
return (
super().get_queryset(request)
.annotate(age=...)
)
def get_age(self, instance):
return instance.age
I am making a CV page,
I want to link my Skill, Language etc class(table) to Main Person table/class,
But for that, I need to submit skill table first because my person table contains the foreign key for skills.
But as per CV form name & personal info comes first.
Also, I can put the whole form on one page but I want to go to the next page for each sub information, so is it possible to pass the request data from one class-based view to another class-based view.
models.py
from django.db import models
from django.core.validators import MinLengthValidator
from django.conf import settings
import datetime
class Workexperience(models.Model):
work = models.CharField(null=True, blank=True,
max_length=256,
help_text='eg: Juniorengineer: at L&T ')
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=False, default=1 )
def __str__(self):
return self.work
class Education(models.Model):
school = models.CharField(max_length=200)
college = models.CharField(null=True, blank=True,max_length=200)
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=False, default=1 )
def __str__(self):
return self.school
class Skills(models.Model):
skill = models.CharField(
max_length=256,
help_text='Add skills sperated by commas eg: programming, Matlab')
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=False, default=1 )
def __str__(self):
return self.skill
class Languages(models.Model):
language = models.CharField(
max_length=256,
help_text='Add language sperated by commas eg: English, Gujarati')
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=False, default=1 )
def __str__(self):
return self.language
class Person(models.Model):
name = models.CharField(
max_length=100,
help_text='Enter a name (e.g. Harry Virani)',
validators=[MinLengthValidator(2, "It must be greater than 1 character")]
)
picture = models.BinaryField(null=True, blank=True, editable=True)
content_type = models.CharField(max_length=256, null=True, blank=True,
help_text='The MIMEType of the file')
profession = models.CharField(
max_length=100,
validators=[MinLengthValidator(2, "It must be greater than 1 character")]
)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default='')
address = models.CharField(max_length=256)
email = models.EmailField(max_length = 254)
phone = models.CharField(
max_length=15,
help_text='Enter a phone number like this (e.g. +91000000000)',
validators=[MinLengthValidator(10, "It must be greater than 10 character")] )
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
facebook = models.URLField(null=True, blank=True, max_length=200,
help_text='enter your facebook URL' )
instagram = models.URLField(null=True, blank=True, max_length=200,
help_text='enter your instagram link URL' )
linkedin = models.URLField(null=True, blank=True, max_length=200,
help_text='enter your Linked link URL' )
skill = models.ManyToManyField(Skills, related_name='skills', default=1)
language = models.ManyToManyField(Languages, related_name='languages', default=1)
edu = models.ManyToManyField(Education, default=1,related_name='edu' )
work = models.ManyToManyField(Workexperience,default=1, blank=True, related_name='works')
# Shows up in the admin list
def __str__(self):
return self.name
views.py
I want to save it in another class which is for creating skill & other models.
class PersonCreateView(LoginRequiredMixin, View):
template_name = 'MYP/form.html'
success_url = 'MYP:myp_create_info'
def get(self, request, pk=None):
personform = PersonForm()
ctx = { 'personform': personform}
return render(request, self.template_name, ctx)
def post(self, request, pk=None) :
# if 'personform' in request.POST:
personform = PersonForm(request.POST, request.FILES or None)
if not personform.is_valid():
ctx = {'personform': personform}
return render(request, self.template_name, ctx)
pform = personform.save(commit=False)
#adding onwer
pform.owner = self.request.user
pform.save()
return redirect(self.success_url, pform.id)
class InfoCreateView(LoginRequiredMixin, View):
template_name = 'MYP/form2.html'
success_url = reverse_lazy('MYP:all')
def get(self, request, pk):
person = get_object_or_404(Person,id=pk)
skill= SkillsForm()
skill_list = Skills.objects.filter(person=person)
ctx = { 'skill':skill, 'skill_list':skill_list }
return render(request, self.template_name, ctx)
def post(self, request, pk):
if 'skill' in request.POST:
skill = SkillsForm(request.POST or None)
if not skill.is_valid() :
ctx = { 'skill':skill}
return render(request, self.template_name, ctx)
person = get_object_or_404(Person,id=pk)
print(person)
skill = Skills(skill=request.POST['skill'], person=person)
skill.save()
print(skill.person)
return redirect('MYP:myp_create_info', pk=pk)
forms.py
class PersonForm(forms.ModelForm):
max_upload_limit = 2 * 1024 * 1024
max_upload_limit_text = naturalsize(max_upload_limit)
# Call this 'picture' so it gets copied from the form to the in-memory model
# It will not be the "bytes", it will be the "InMemoryUploadedFile"
# because we need to pull out things like content_type
picture = forms.FileField(required=False, label='File to Upload <= '+max_upload_limit_text)
upload_field_name = 'picture'
# Hint: this will need to be changed for use in the ads application :)
class Meta:
model = Person
fields = ['name', 'profession', 'picture', 'address', 'email', 'phone','facebook','linkedin','instagram'] # Picture is manual
# Validate the size of the picture
def clean(self) :
cleaned_data = super().clean()
pic = cleaned_data.get('picture')
if pic is None : return
if len(pic) > self.max_upload_limit:
self.add_error('picture', "File must be < "+self.max_upload_limit_text+" bytes")
# Convert uploaded File object to a picture
def save(self, commit=True) :
instance = super(PersonForm, self).save(commit=False)
# We only need to adjust picture if it is a freshly uploaded file
f = instance.picture # Make a copy
if isinstance(f, InMemoryUploadedFile): # Extract data from the form to the model
bytearr = f.read();
instance.content_type = f.content_type
instance.picture = bytearr # Overwrite with the actual image data
if commit:
instance.save()
return instance
class WorkexperienceForm(forms.ModelForm):
class Meta:
model = Workexperience
fields = ['work']
class EducationForm(forms.ModelForm):
class Meta:
model = Education
fields = ['school','college']
class SkillsForm(forms.ModelForm):
class Meta:
model = Skills
fields = ['skill']
class LanguagesForm(forms.ModelForm):
class Meta:
model = Languages
fields = ['language']
Ignore the rest of the code it is just for image handling....
This is what I want to do but I know it is the wrong format
I want to just add id for everything later.
In my opinion, your models are messed up. Here is how I would have write them :
class WorkExperience(models.Model):
work = models.CharField(
blank=True,
max_length=256,
help_text='eg: Juniorengineer: at L&T'
)
def __str__(self):
return self.work
class Education(models.Model):
school = models.CharField(max_length=200)
college = models.CharField(blank=True, max_length=200)
def __str__(self):
return self.school
class Skill(models.Model):
name = models.CharField(
max_length=256,
help_text='Add a skill name (eg: Programming)'
)
def __str__(self):
return self.name
class Language(models.Model):
name = models.CharField(
max_length=256,
help_text='Add a language name (eg: Gujarati)'
)
def __str__(self):
return self.name
class Person(models.Model):
name = models.CharField(
max_length=100,
help_text='Enter a name (e.g. Harry Virani)',
validators=[MinLengthValidator(2, "It must be greater than 1 character")]
)
# [...Other fields...]
skills = models.ManyToManyField(Skill, related_name='persons', blank=True)
languages = models.ManyToManyField(Language, related_name='persons', blank=True)
educations = models.ManyToManyField(Education, related_name='persons', blank=True)
work_experiences = models.ManyToManyField(WorkExperience, related_name='persons', blank=True)
def __str__(self):
return self.name
Then I need to see your forms.py to better understand how you handle it in your view.
I'm trying to save some data through a form and I'm trying to use a
FormSet. The data to be saved is an invoice which contains a Product
and it's Details.
I was able to render everything in one place (this wasn't simple) and
to save the invoice and the detail to the DB. For some reason, the
Detail table is saving the Product ID but not the Invoice ID. This is
my models.py:
class Detail(models.Model):
invoice = models.ForeignKey(Invoice, null=True, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
subtotal = models.DecimalField(max_digits=9 , null=True, decimal_places=2)
class Invoice(models.Model):
date = models.DateField(default=datetime.date.today)
number = models.CharField(max_length=10, default='0000000000')
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
products = models.ManyToManyField(Product, null=True, blank=True)
total = models.DecimalField(max_digits=9 , null=True, decimal_places=2)
And this is my views.py:
def invoice_new(request):
DetailFormSet = formset_factory(DetailForm, extra=2)
if request.method == "POST":
invoice_form = InvoiceForm(request.POST)
detail_formset = DetailFormSet(request.POST)
if invoice_form.is_valid() and detail_formset.is_valid():
invoice = invoice_form.save(commit=False)
invoice.save()
for details in detail_formset:
details.save()
return redirect('invoice_detail', pk=invoice.pk)
else:
return redirect('invoice_error')
else:
invoice_form = InvoiceForm()
detail_formset=formset_factory(DetailForm, extra=2)
return render(request, 'invoice/invoice_edit.html', {'invoice_form': invoice_form, 'detail_form': detail_formset} )
I tried adding this to the body of the for loop:
details.invoice_id = invoice.pk
invoice.id prints OK, but it won't save the number to the DB. I don't see how it picks up product id just fine but not invoice's.
I'm adding forms.py
class InvoiceForm(forms.ModelForm):
class Meta:
model = Invoice
fields = ['date','number','supplier']
total = forms.DecimalField(disabled=True)
class DetailForm(forms.ModelForm):
class Meta:
model = Detail
fields = ['product','quantity']
You can set the invoice id before saving the form.
if invoice_form.is_valid() and detail_formset.is_valid():
invoice = invoice_form.save(commit=False)
invoice.save()
for details in detail_formset:
details.invoice = invoice # Set the invoice
details.save()
More than one day trying to figure out on how to use the Django Admin list_filter on a QuerySet using .extra()
In the AdAdmin I need to add one new column 'ad_hist_status_id' from the model AdHist so I can use this portion of the code on SomeListFilter:
def queryset(self, request, queryset):
return queryset.filter(ad_hist_status_id=self.value())
It looks like impossible. Doing this with sql is easy:
select a.*, ah.ad_hist_status_id from ad_ad a
join ad_adhist ah on ah.ad_id = a.id
where
ah.datetime_end is null
order by a.id DESC
Until now I cannot make to work this SomeListFilter in the Django Admin, the error is:
FieldError at /admin/ad/ad/
Cannot resolve keyword 'ad_hist_status_id' into field.
Choices are: addetailscategories, address, adhist, adphotos,
adprice, adscheduleinfo, age, comment, county, county_id,
date_inserted, date_updated, description, district, district_id,
email, id, lat, lng, name, parish, parish_id, schedule_type,
schedule_type_id, telephone, title, user_inserted, user_inserted_id,
user_updated, user_updated_id
My question is, how do I effectively add a new column to a QuerySet and then how can I query this new QuerySet with the new column?
Some portions of my code bellow
The Models:
class Ad(models.Model):
title = models.CharField(max_length=250)
name = models.CharField(max_length=100)
description = models.TextField()
age = models.IntegerField(blank=True, null=True)
telephone = models.CharField(max_length=25)
email = models.EmailField()
district = models.ForeignKey(District)
county = ChainedForeignKey(County, chained_field="district", chained_model_field="district", sort=True) # smart_selects app
parish = ChainedForeignKey(Parish, chained_field="county", chained_model_field="county", sort=True) # smart_selects app
address = models.CharField(max_length=250, null=True, blank=True)
lat = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
lng = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
schedule_type = models.ForeignKey(AdScheduleType)
comment = models.TextField(null=True, blank=True)
user_inserted = models.ForeignKey(User, null=True, blank=True, related_name='user_inserted_ad')
date_inserted = models.DateTimeField(auto_now_add=True)
user_updated = models.ForeignKey(User, null=True, blank=True, related_name='user_updated_ad')
date_updated = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.name
class AdHist(models.Model):
ad = models.ForeignKey(Ad)
datetime_begin = models.DateTimeField()
datetime_end = models.DateTimeField(null=True, blank=True)
ad_hist_status = models.ForeignKey(AdHistStatus)
ad_hist_change_reason = models.ForeignKey(AdHistChangeReason)
comment = models.TextField(null=True, blank=True)
user_inserted = models.ForeignKey(User, null=True, blank=True, related_name='user_inserted_ad_hist')
date_inserted = models.DateTimeField(auto_now_add=True)
user_updated = models.ForeignKey(User, null=True, blank=True, related_name='user_updated_ad_hist')
date_updated = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.ad.name
The Admin:
class SomeListFilter(admin.SimpleListFilter):
title = _('Approval State')
parameter_name = 'ad_hist_status_id'
def lookups(self, request, model_admin):
return (
('1', _('Approved')),
('4', _('Not Approved')),
)
def queryset(self, request, queryset):
return queryset.filter(ad_hist_status_id=self.value())
class AdAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'title', 'age', 'telephone',
'email', 'district', 'county', 'parish',
'ad_status', 'ad_hist_change_reason', 'comment',
'user_inserted', 'date_inserted', 'user_updated',
'date_updated', 'ad_hist_status_id')
readonly_fields = ('ad_status', 'id', 'ad_hist_change_reason',
'ad_hist_status_id')
list_filter = (SomeListFilter,)
def get_queryset(self, request):
qs = super(AdAdmin,self).get_queryset(request).extra(select={'ad_hist_status_id': 'select ad_hist_status_id from ad_adhist where ad_adhist.ad_id = ad_ad.id and ad_adhist.datetime_end is null'},)
return qs
def ad_hist_status_id(self, inst):
return inst.ad_hist_status_id
Can someone give me a clue?
Best Regards
If I understand your question right, you are looking for this:
from django.db.models import F
def queryset(self, request, queryset):
return queryset.filter(ad_hist_status__id=F('id'))
The F expression is used to reference a field from the same model, and to refer a field from a related model, you need to use an underscore (__).
Take a closer look to Django field lookups. Look at what the docs say:
Basic lookups keyword arguments take the form field__lookuptype=value. (That’s a double-underscore).
You want to take the AdHist related object from Ad, which has a related AdHistStatus, and get its id.
So you should access this id field like:
def ad_hist_status_id(self, instance):
return instance.adhist.ad_hist_status.id
Or you can list it directly with the double-underscore syntax:
class AdAdmin(admin.ModelAdmin):
list_display = (..., 'adhist__ad_hist_status__id')