Django - Keep track of comments - python

I am building a web app, where each product has its own "Profile". I need to add to the model some kind of field where i can add "Comments", with date and text, for keeping track of info such as change in formula, change of provider, change in price, etc.
Any ideas?
models.py
from django.db import models
# Create your models here.
class Horse(models.Model):
name = models.CharField(max_length=255)
nacimiento = models.DateField(blank=True, null=True)
nro = models.IntegerField()
event = models.TextField()
slug = models.SlugField(unique=True)
def __str__(self):
return '%s-%s' % (self.name, self.nro)
So for every event that happens, i need a new entrance with the description provided in the text field.

class HorseTracker(models.Model):
horse = models.ForeignKey(Horse, on_delete=models.CASCADE, related_name='horse')
comment = models.CharField(max_length=128)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
Each time you change something in your model you can create new instance of HorseTracker with description of changes you've made.
To make it more usefull you can use TabularInline in your HorseAdmin
class HorseTrackerInline(admin.TabularInline):
model = HorseTracker
class HorseAdmin(admin.ModelAdmin):
list_display = ['name', 'nacimiento', 'nro', 'event', 'slug', ]
inlines = [ HorseTrackerInline, ]

If you want to track various models I would suggest to use something like django-simple-history to keep track of the changes in your model.
Adding a history field to the model lets you save all the changes made to the fields and then access the history. If you want to add a custom message you can add fields to the historical model, and in a signal set the message.
from simple_history.models import HistoricalRecords
class MessageHistoricalModel(models.Model):
"""
Abstract model for history models tracking custom message.
"""
message = models.TextField(blank=True, null=True)
class Meta:
abstract = True
class Horse(models.Model):
name = models.CharField(max_length=255)
birthdate = models.DateField(blank=True, null=True)
nro = models.IntegerField()
event = models.TextField()
slug = models.SlugField(unique=True)
history = HistoricalRecords(bases=[MessageHistoricalModel,])
Then using signals you can get changes using diff and then save a custom message stating the changes an who made them.
from django.dispatch import receiver
from simple_history.signals import (post_create_historical_record)
#receiver(post_create_historical_record)
def post_create_historical_record_callback(sender, **kwargs):
history_instance = kwargs['history_instance'] # the historical record created
# <use diff to get the changed fields and create the message>
history_instance.message = "your custom message"
history_instance.save()
You could generate a pretty generic signal that works for all your models tracked with a 'history' field.
Note: I renamed "nacimiento" as "birthdate" to keep consistency in naming all the fields in english.

Related

In Django Tables2, how do you make a column show text from a table referenced by a foreign key?

After reading all the docs and answers I can find, and burning a whole day, I still can't make this work. Using Django Tables2, I want to show a list of instruments; the instruments table includes a foreign key to an instrumentsType table. When I list the instruments and their attributes, I want to use the foreign key to substitute the textual instrument type description from the other table. I have tried every combination of double underscores and other accessor techniques, but so far all I get is the dreaded -- in the column. (Displaying just the record ID works).
from .models import Instrument
from django_tables2 import A
from instrumenttypes.models import InstrumentType
class InstrumentTable(tables.Table):
id = tables.LinkColumn('instrument_details', args=[A('station_id')])
class Meta:
model = Instrument
template_name = "django_tables2/bootstrap.html"
fields = ("id", "instrument", "nickname", "serialNo",
"instrument__instrumenttype_id__instrumenttypes__id_instrumentType" )
The models involved are:
Instruments model.py
from django.db import models
from instrumenttypes.models import InstrumentType
from stations.models import Station
# Create your models here.
class Instrument(models.Model):
instrument = models.CharField(max_length=40)
instrumenttype = models.ForeignKey(InstrumentType, on_delete=models.CASCADE, null=True)
station = models.ForeignKey(Station, on_delete=models.CASCADE, default=1)
serialNo = models.CharField(max_length=60, null=True, blank=True)
dateAdded = models.DateTimeField("Date Added", null=True, blank=True)
dateRemoved = models.DateTimeField("Date Removed", null=True, blank=True)
status = models.CharField(max_length=10, null=True, blank=True)
nickname = models.CharField(max_length=40, null=True, blank=True)
InstrumentTypes model.py
from django.db import models
class InstrumentType(models.Model):
instrumentType = models.CharField(max_length=40)
Resulting output:
ID Instrument Nickname SerialNo Instrumenttype
4 instr2 nock2 123 —
The most relevant online references I have found are here and here; but having tried the suggestions, no luck. What am I missing?
I've been struggling to get something working too (but I finally did), and I found the examples too brief.
I think you want to get rid of this stuff in the Meta class
"instrument__instrumenttype_id__instrumenttypes__id_instrumentType"
I think Meta.fields should just be a list of field names, and that you refer to the attribute in the other table from the point of view of the type of object you will later pass in to the IntrumentTable constructor (and that is named in the Meta.model attribute:
from django_tables2.utils import Accessor
class InstrumentTable(tables.Table):
instrument_type = tables.Column(accessor=Accessor('instrumenttype.name'))
class Meta:
model = Instrument
template_name = "django_tables2/bootstrap.html"
fields = ("id", "instrument", "nickname", "serialNo", "insrument_type")
Then, in view, make an instance of InstrumentTable
def myview(request):
table_to_render = InstrumentTable(Instrument.objects)
return render(request, sometemplate, {table: table_to_render})
You didn't show your view, and I know there may be a different way. If you have the whole thing in a repo somewhere, leave a link.

Field 'id' expected a number - Uploading ForeignKey to django-import-export

I am trying to import data from an csv file into a django db using django-import-export. My problem is trying to upload data with a ForeignKey as an object. I have migrated, followed docs, and still no solution. You can see my error below in the django admin:
Here is my csv data with a blank 'Id' column:
models.py
from django.db import models
from django.shortcuts import reverse
from urllib.parse import urlparse
class States(models.Model):
name = models.CharField(max_length=96, blank=False, unique=True)
abbrv = models.CharField(max_length=2, null=True, blank=True)
class Meta:
ordering = ['name']
verbose_name = 'State'
verbose_name_plural = 'States'
def __str__(self):
return f'{self.name}'
class Person(models.Model):
last_name = models.CharField(
max_length=255, help_text="Enter your last name.")
first_name = models.CharField(
max_length=255, help_text="Enter your first name or first initial.")
address = models.CharField(
max_length=255, blank=True, help_text="Enter your street address.")
city = models.CharField(
max_length=255, blank=True, help_text="Enter your city.")
state = models.ForeignKey('States', to_field='name', on_delete=models.SET_NULL, null=True)
zipcode = models.CharField(max_length=50)
website = models.URLField(
max_length=255, blank=True)
profession = models.CharField(max_length=50, blank=True)
# META CLASS
class Meta:
verbose_name = 'Person'
verbose_name_plural = 'Persons'
ordering = ['last_name', 'first_name']
# TO STRING METHOD
def __str__(self):
"""String for representing the Model object."""
return f'{self.last_name}, {self.first_name}'
admin.py:
from django.contrib import admin
from .models import Person, States
from import_export.admin import ImportExportModelAdmin
from import_export.widgets import ForeignKeyWidget
from import_export import fields, resources
class PersonResource(resources.ModelResource):
state = fields.Field(
column_name='state',
attribute='state',
widget=ForeignKeyWidget(States, 'name'))
class Meta:
model = Person
class PersonAdmin(ImportExportModelAdmin):
list_display = ('last_name', 'first_name', 'state')
search_fields = ('first_name', 'last_name' )
resources_class = PersonResource
admin.site.register(Person, PersonAdmin)
admin.site.register(States)
I think you need to specify both in your question here, as well as to Django how you want the id field treated.
Do you want it propagated with the Django id or pk (sometimes the same sometimes not)? Then you would have id=self.id or id=self.pk somewhere in your view for the datatable.
Do you want your database to create a unique key?
You would need to add some functionality someplace to tell Django how to fill in that field.
Also, if you want it to create an id different from the Django id or pk then you would need to add the field to your model.
https://docs.djangoproject.com/en/3.1/ref/forms/validation/
https://docs.djangoproject.com/en/3.1/ref/validators/
https://docs.djangoproject.com/en/3.1/ref/forms/api/
Or, perhaps after Validation of the form, when you create the object. Add something to the effect of id=[database function to create unique id].
Another solution might be a templateTag or templateFilter to create a value on the form side if you want to create the id based on info contained in the form. Like combining last 4 of name with time of submission.
https://docs.djangoproject.com/en/3.1/ref/templates/builtins/
https://docs.djangoproject.com/en/3.1/howto/custom-template-tags/
Having just re-read your question, also, I'm not sure but you might be asking if the database can support an embedded reference to another object. Is ID a reference to another model's key? That's a whole different question. And it is database specific.
Last Suggestion: Perhaps a re-read of:
https://docs.djangoproject.com/en/3.1/ref/forms/fields/#fields-which-handle-relationships
This error is occur because your id did not received an id or int value it received a str type of value Wyoming try to pass int value in id
Update
just update your PersonResource Meta class like this
class PersonResource(resources.ModelResource):
state = fields.Field(
column_name='state',
attribute='state',
widget=ForeignKeyWidget(States, 'name'))
class Meta:
model = Person
import_id_fields = ['id']
The default field for object identification is id, you can optionally
set which fields are used as the id when importing
check official doc. for more information.

How to edit a property in Django Admin?

I have a model with an attribute that is connected to another model as follow:
class Book(models.Model):
synced = models.OneToOneField('SyncedBook'
related_name='internal',
on_delete=models.CASCADE)
# some more attributes here...
#property
def book_address(self)
return self.synced.book_address
However, the book_address is a also a FK in the SyncedBook table as follow:
book_address = models.ForeignKey('Address', db_index=True, null=True, blank=True,
related_name='address_book', on_delete=models.PROTECT)
I don't know and understand how to be able to edit the book_address through the Django admin page in class BookingAdmin(admin.ModelAdmin), even though I have read over the documentation. At first I have the attribute as readonly, but now I want to be able to edit it and save the new address from the Address table. Is there a way to make it happen through the class BookingAdmin(admin.ModelAdmin) and how? Any example and solution would be appreciate
Model properties are typically used for presenting logically defined data for a particular model instance and not necessarily storing data on the model instance itself.
An example of when to use a model property is as follows:
# Defines a product instance
class Product(model.Models):
name = models.CharField(max_length=100)
description = models.TextField()
active = models.BooleanField(default=True)
cost = models.DecimalField(max_digits=5, decimal_places=2)
price = models.DecimalField(max_digits=5, decimal_places=2)
# calculate profits on product
#property
def profit(self)
p = self.price - self.cost
return p
In your case, you are trying to actually be able to modify data against a related model instance within the django admin. To me this sounds like more specifically an Inline (click here for documentation)
So in your case, you would need to create something like the following to your admin.py file:
class SyncedBookInline(admin.TabularInline):
model = BookInline
#admin.Register(Book)
class BookAdmin(admin.ModelAdmin):
# all your model admin settings
inlines = [SyncedBookInline]
Additional Info:
The Inline solution should still work for you. Please see the working code listed below:
models.py:
from django.db import models
class Hero(models.Model):
name = models.CharField(max_length=50)
class HeroAcquaintance(models.Model):
name = models.CharField(max_length=50)
hero = models.OneToOneField(Hero, on_delete=models.CASCADE)
admin.py:
from django.contrib import admin
from .models import *
class HeroAcquaintanceInline(admin.TabularInline):
model = HeroAcquaintance
#admin.register(Hero)
class HeroAdmin(admin.ModelAdmin):
list_display = (
'name',
)
inlines = [HeroAcquaintanceInline]
#admin.register(HeroAcquaintance)
class HeroAcquaintanceAdmin(admin.ModelAdmin):
list_display = (
'name',
)
Screenshot:

Assigning current user in rest framework view

I have been getting my head around these basics but I am not getting it right. I am trying to associate my view to my user model using team which is a foreign key. When I try to create of a gps, I get an error saying "team is a required field" but instead it should be read only. The team attribute should be filled automatically with the id of the currentUser
Model
class User(models.Model):
first_name = models.CharField(max_length=200,blank=False)
last_name = models.CharField(max_length=200, blank=False)
class Gps(models.Model):
location = models.CharField(max_length=200,blank=False)
team= models.ForeignKey(User, on_delete=models.CASCADE)
serializers
class GpsSerializer(serializers.ModelSerializer):
class Meta:
model = Gps
fields = ('id','location','team')
view
class Gps_list(generics.ListCreateAPIView):
queryset = Gps.objects.all()
serializer_class = GpsSerializer
team = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
There are two changes needed. First, team field definition should be moved to serializer class instead of view. Second, you should use Django's contrib.auth.User model instead of your definition of User, as because serializers.CurrentUserDefault() will bring request.user only. So you should remove your User definition and import that to your models.py:
from django.contrib.auth.models import User
Further steps would be to replace read_only=True with queryset=User.objects.all() to allow create.

Django Admin: Add foreign keys at same time as model

I am a novice in Django and I'm learning the ropes of the admin interface. I have a model with several foreign keys. These foreign keys then reference other foreign keys. On the admin website after I register the Property model and then try to add it I am given a dropdown box for each foreign key model. However this dropdown box only lists existing foreign keys. (http://i.stack.imgur.com/e5LCu.png)
What would be great is if instead of a dropdown box there were extra fields so I could add the foreign key models as I add the property model. That way I wouldn't have to manually add foreign keys and then go back and add some more, and then go back and finally add the property data.
How can I do this? This feels like a simple enough question but after intense Googling I still can't find the answer, so I apologize in advance.
Example of two of my models:
class Address(models.Model):
state = models.ForeignKey('State')
address1 = models.CharField(max_length=200)
address2 = models.CharField(max_length=200)
city = models.CharField(max_length=200)
postal_code = models.CharField(max_length=200)
class Property(models.Model):
address = models.ForeignKey('Address', blank=True, null=True)
borrower = models.ForeignKey('Person', blank=True, null=True)
company = models.ForeignKey('Company', blank=True, null=True)
contract = models.ForeignKey('Contract', blank=True, null=True)
loan_balance = models.IntegerField()
name = models.CharField(max_length=200)
primary_email = models.CharField(max_length=200)
primary_phone = models.CharField(max_length=200)
property_no = models.IntegerField()
Example of my admin.py:
# Register your models here.
class PropertyAdmin(admin.StackedInline):
model = Property
class PersonAdmin(admin.StackedInline):
model = Person
class CompanyAdmin(admin.StackedInline):
model = Company
class ContractAdmin(admin.StackedInline):
model = Contract
class CompletePropertyAdmin(admin.ModelAdmin):
inlines = [PropertyAdmin, PersonAdmin, CompanyAdmin, ContractAdmin]
admin.site.register(Property)
One solution to the problem can be, to create a custom form with fields from both the models and at the time of saving the values, first create the instance of Address model and then with that instance save your final Property model.

Categories

Resources