Django Admin - Show form json input value as text insted string - django

Considering I have a model like:
MyStore = (
id = 1,
name = 'Foobar',
information_as_json = {
'open_at': datetime.now(),
'close_at': datetime.now() + timedelta('+1 day'),
'workers' : {
'Person1' : 'Owner',
'Person2' : 'Boss',
'Person3' : 'Boss',
}
})
Inside Django admin forms, for every field is generated an input, but for the field "information_as_json", I don't want to show it as a string or as JSON. That is because the users who are accessing this store admin page, need to read the field 'information_as_json' easier since no one can edit these values because it is generated in another part of the application.
Is it possible to convert these values to a "div" or a plain text? The contents would be:
This store opens at: {information_as_json.open_at}
This store close at: {information_as_json.close_at}
And for the workers, iterate through keys and values:
for key, value in information_as_json.workers:
Worker {key} has the role: {value}
I'm a beginner at Django, so I'm struggling a little with this part.
Every help would be appreciated :D

I would suggest approaching the model a little differently. Rather than storing the opening and closing hours as JSON they can just be fields directly on the store model. The the workers can be a JSONfield [docs] containing name/role pairs. If you're using PostgreSQL for your database you could even use HStoreField [docs], which might be more appropriate.
Here's how I would write a similar model.
class Store(models.Model):
name = models.CharField(max_length=512, unique=True)
workers = models.JSONField(blank=True, default=dict, editable=False)
closing = models.TimeField(blank=True, null=True, editable=False)
opening = models.TimeField(blank=True, null=True, editable=False)
To display the details in the Django admin we just need to define a property which returns the correct string.
#mark_safe
def details(self):
roles = [
f'{x} has the role: {y}'
for x, y in self.workers.items()
]
return '<br>'.join([
f'This store opens at: {self.opening:%-H:%M}',
f'This store closes at: {self.closing:%-H:%M}',
] + roles)
This method can then be referenced in the ModelAdmin and used like a read-only field.
#admin.register(Store)
class StoreAdmin(admin.ModelAdmin):
list_display = ['name', 'opening', 'closing']
fields = ['name', 'details']
readonly_fields = ['details']

Related

Model Admin Error: Cannot Exclude Field because it is a Foreign Key to the parent model

My goal is to be able to select a location and Input part numbers without seeing this quote field. I dont even completely understand what this select box is looking for. I have Quote objects saved and yet these are not coming up as selectable options. Not that I want them to, Im just saying. My thinking regarding the seelctable options is that this would be auto-populated? You can probably tell my confusion even in my explanation. Ultimately, I dont want to see a select box at all as Im not really interested in whatever this pointing to, but just for kicks would like to know what it is trying to point to.
quote/Models.py
class Quote(models.Model):
QUOTE_ENVIRONMENTS = (
('testing', 'Test'),
('production', 'Production')
)
SALES_SOURCE=((1, 'Marketplace'),
(2, 'Webstore'),
(3, 'Physical Store'),
(4, 'Phone')
)
environment = models.CharField(max_length=20, choices=QUOTE_ENVIRONMENTS, default="testing")
sales_source = models.IntegerField(choices=SALES_SOURCE, null=True)
order_notes = models.TextField(blank=True)
locations = models.ManyToManyField('products.ProductSelection')
products/models.py
class Product(models.Model):
pass
class Warehouse(models.Model):
pass
class ProductSelection(models.Model):
location = models.ForeignKey('Warehouse', on_delete = models.CASCADE)
product = models.ManyToManyField('Product')
Admin.py
class ProductOrderForm(forms.ModelForm):
locations = forms.ModelChoiceField(queryset= Warehouse.objects.all())
part_number = forms.IntegerField()
def clean_product_id(self):
cd = self.cleaned_data
logger.info(cd)
value = cd['part_number']
if value not in Products.objects.list_part_numbers():
raise forms.ValidationError("Not a valid partnumber")
class ProductSelectionTabularInline(admin.TabularInline):
form = ProductOrderForm
model = Quote.locations.through
class QuoteAdmin(admin.ModelAdmin):
list_display=['id', 'environment', 'order_notes','sales_source']
list_editable = ['environment', 'sales_source', 'order_notes']
inlines = [ProductSelectionTabularInline]
exclude=['quote']
Error when using exclude attr.
ERRORS:
<class 'orders.admin.ProductSelectionTabularInline'>: (admin.E201) Cannot exclude the field 'quote', because it is the foreign key to the parent model 'orders.Quote'.
I dont want the left most box. Thanks for your help
I figure out that the field to the left is the ProductSelection instance. I confused myself by adding the other 2 form widgets. So this does not allow me to do what I want which is to edit the parts to the locations and add it to the form for creating a quote.

Using Tag model to create ManytoMany relationship

I am trying to fetch data where the column value "tag" belongs to list from the table "UserBookmark".
UserBookmark.objects.filter(tag__in = ['Java','Android'])
but this returns QuerySet[](null set) whereas I do have data matching this query in table
<QuerySet [<UserBookmark: 21 user12 http://careers.bankofamerica.com/ [u'Java']>,<UserBookmark: 22 user12 http://aehlke.github.io/tag-it/examples.html [u'Data Science,Python']>,<UserBookmark: 23 user13 https://github.com/Azure/azure-quickstart-templates [u'Android']>, <UserBookmark: 24 user14 https://github.com/sunnykrGupta/Bigquery-series [u'Python']>, <UserBookmark: 25 user14 https://github.com/ctfs/write-ups-2017 [u'Data Analytics']>]>
models.py
class UserBookmark(models.Model):
user = models.ForeignKey(User)
bookmark = models.URLField()
tag = models.CharField(max_length = 100)
def __str__(self):
return '%i %s %s %s'%(self.id,self.user,self.bookmark,self.tag)
i have modified my models.py
class UserBookmark(models.Model):
user = models.ForeignKey(User)
bookmark = models.URLField()
tags = models.ManyToManyField('Tag',blank=True)
def __str__(self):
return '%i %s %s'%(self.id,self.user,self.bookmark)
class Tag(models.Model):
name = models.CharField(max_length=100, unique=True)
But when i run python manae.py migrate after python managepy makemigrations, I get this error:
ValueError: Cannot alter field bookmark.UserBookmark.tags into bookmark.UserBookmark.tags - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)
What am i doing wrong here?
The problem is not in your query, but in the way you are storing your data. You have a single CharField, and you seem to be populating it by simply converting a list to a string. So your records contain for example the literal string "[u'Data Science,Python']".
If you want to store this kind of tag, you need to store the tags separately. One way to do this would be to set up a separate Tag model and use a many-to-many relationship. There are various third-party packages that do this for you - one example is django-taggit.
Try to pluralize, maybe using tags__in

Query intermediate through fields in django

I have a simple Relation model, where a user can follow a tag just like stackoverflow.
class Relation(models.Model):
user = AutoOneToOneField(User)
follows_tag = models.ManyToManyField(Tag, blank=True, null=True, through='TagRelation')
class TagRelation(models.Model):
user = models.ForeignKey(Relation, on_delete=models.CASCADE)
following_tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
pub_date = models.DateTimeField(default=timezone.now)
class Meta:
unique_together = ['user', 'following_tag']
Now, to get the results of all the tags a user is following:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
tags_following = kakar.relation.follows_tag.all()
This is fine.
But, to access intermediate fields I have to go through a big list of other queries. Suppose I want to display when the user started following a tag, I will have to do something like this:
kakar = CustomUser.objects.get(email="kakar#gmail.com")
kakar_relation = Relation.objects.get(user=kakar)
t1 = kakar.relation.follows_tag.all()[0]
kakar_t1_relation = TagRelation.objects.get(user=kakar_relation, following_tag=t1)
kakar_t1_relation.pub_date
As you can see, just to get the date I have to go through so much query. Is this the only way to get intermediate values, or this can be optimized? Also, I am not sure if this model design is the way to go, so if you have any recomendation or advice I would be very grateful. Thank you.
You need to use Double underscore i.e. ( __ ) for ForeignKey lookup,
Like this :
user_tags = TagRelation.objects.filter(user__user__email="kakar#gmail.com").values("following_tag__name", "pub_date")
If you need the name of the tag, you can use following_tag__name in the query and if you need id you can use following_tag__id.
And for that you need to iterate through the result of above query set, like this:
for items in user_tags:
print items['following_tag__name']
print items['pub_date']
One more thing,The key word values will return a list of dictionaries and you can iterate it through above method and if you are using values_list in the place of values, it will return a list of tuples. Read further from here .

Django Custom Primary Key Tries to insert Null

There are some constraints to this problem. We currently use a production database, with live Virtual Machine Statistics. We are trying to create a django interface, that interfaces with the tables we want our administrators to be able to edit. Thus, migrations are out of the question, for unless I have come to understand migrations wrong it will affect the current database structure and or data.
I matched the database structure exactly in my models.py file. However I have run into a few issues. One of the issues I have run into is when I try to add a new item under the admin control panel it will give me an integrity error as it is attempting to insert a null value for the field I have set as the primary key in the models.py file.
We are currently using an oracle database.
My Models.py not all of it but a sample of it.
class License(models.Model):
license_id = models.AutoField(primary_key = True, editable = False, db_column='license_id')
license_authority_id = models.ForeignKey(License_authoritie, on_delete = models.PROTECT, db_column='license_authority_id')
product = models.CharField(max_length = 20)
class Meta:
managed = False
db_table = 'licenses'
ordering = ['product']
def __unicode__(self): # Python 3: def __str__(self):
return self.product
class Vm_license(models.Model):
vm_license_id = models.AutoField(primary_key = True, db_column='vm_license_id')
vm_id = models.ForeignKey(Vm, on_delete = models.PROTECT, db_column='vm_id')
license = models.ManyToManyField(License)
class Meta:
managed = False
db_table = 'vm_licenses'
The error I get:
Request Method: POST
Request URL: http://127.0.0.1:8000/admin/portal/vm_license/add/
Django Version: 1.6.5
Exception Type: IntegrityError
Exception Value:
ORA-01400: cannot insert NULL into ("DEV"."VM_LICENSES"."VM_LICENSE_ID")
On top of that I have run into another problem.
For these two tables, under the vm_licenses section in the admin panel which is a table that holds all VM's and their assigned licenses. I need the ability to select multiple licenses at a time for each vm_id under the add section of the admin panel but i'm not quite sure how to do this.
admin.py code
class vm_license_admin(admin.ModelAdmin):
#list_display = ('vm_id', 'license_id')
list_display = ('vm_id',)
search_fields = ('vm_id__vm_name',)
ordering = ('vm_id',)
filter_horizontal = ('license',)
admin.site.register(Vm_license, vm_license_admin)
I also made an oracle trigger to auto increment a primary key if there is none, but im still getting the same error.
CREATE OR REPLACE TRIGGER license_trigger
BEFORE INSERT ON vm_licenses
FOR EACH ROW
BEGIN
SELECT vm_license_seq.nextval
INTO :new.vm_license_id
FROM dual;
END;
to be more percise I am using a manytomany field and it displays correctly when I goto add a new item before clicking save and getting the null error, however if I goto an existing item it will say table or view doesnt exist.
I was going to comment on your question, but I do not have the reputation yet...
but can I suggest you post your relevant admin.py code? Perhaps there is something within it relating to the Null PK error.
With regards to the second part, a ManyToManyField sounds more suitable.

I do not understand this error involving two objects with a many-to-many relation with one another

I am implementing a web interface for email lists. When a list administrator logs in, the site will visually display which lists they are an owner of and corresponding information about the lists. For this I have decided to have two tables:
1) An owner table which contains entries for information about list administrators. Each of these entries contains a 'ManyToManyField' which holds the information about which lists the owner in any given entry is an administrator for.
2) A list table which contains entries with information about each email list. Each entry contains the name of the list a 'ManyToManyField' holding information about which owners are administrators the list.
Here is the code in models.py:
from django.db import models
class ListEntry(models.Model):
name = models.CharField(max_length=64)
owners = models.ManyToManyField('OwnerEntry')
date = models.DateTimeField('date created')
class Meta:
ordering = ('name',)
class OwnerEntry(models.Model):
name = models.CharField(max_length=32)
lists = models.ManyToManyField('ListEntry')
class Meta:
ordering = ('name',)
I have already set up a simple local database to create a basic working website with. I have populated it with test entries using this code:
from list_app.models import *
from datetime import *
le1 = ListEntry(
name = "Physics 211 email list",
date = datetime.now(),
)
le1.save()
le2 = ListEntry(
name = "Physics 265 email list",
date = datetime(2014,1,1),
)
le2.save()
oe1 = OwnerEntry(
name = 'wasingej',
)
oe1.save()
oe1.lists.add(le1,le2)
le1.owners.add(oe1)
le2.owners.add(oe1)
oe2 = OwnerEntry(
name = 'doej',
)
oe2.save()
oe2.lists.add(le1)
le1.owners.add(oe2)
Here is where my error occurs: When the user has logged in via CAS, I have them redirected to this page in views.py:
def login_success(request):
u = OwnerEntry(name=request.user)
print(u.name)
print(u.lists)
return HttpResponse("login success!")
At the line 'print(u.lists)', I get the error "" needs to have a value for field "ownerentry" before this many-to-many relationship can be used.
What am I doing wrong here?
Your model structure is broken, for a start. You don't need ManyToManyFields on both sides of the relationship, only one - Django will provide the accessor for the reverse relationship.
Your issue is happening because you are not querying an existing instance from the database, you are instantiating an unsaved one. To query, you use model.objects.get():
u = OwnerEntry.objects.get(name=request.user.username)
You need to provide the actual class to the ManyToManyField constructor, not a string.
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/