Dynamic change element class in template based on model choices - css

I want to dynamically change an element class inside the template based on my model.
Here is my model:
class StockFlow(models.Model):
TYPE_CHOICES = (
(None, _('- Selecione -')),
('Entrada', (
(1, _('Pedido')),
(2, _('Pegou emprestado')),
(3, _('Entrada simples')),
)
),
('Saida', (
(4, _('Venda')),
(5, _('Emprestado')),
(6, _('Saida simples')),
(7, _('Avaria ou perda')),
(8, _('Presente')),
)
),
)
stock = models.ForeignKey('Stock')
value = models.IntegerField(null=True)
quantity = models.IntegerField(null=True)
date = models.DateField()
type = models.IntegerField(max_length=1, choices=TYPE_CHOICES, default=-1)
description = models.TextField(null=True)
I want to change the class based in if is "Entrada" or "Saida". How can I make this conditional statement to set my css class?

You should probably use a combination of Django forms and template context to accomplish this functionality...
Create a ModelForm from class StockFlow.
from django.forms import ModelForm
class StockFlowForm(ModelForm):
class Meta:
model = StockFlow
def __init__(self, *args, **kwargs):
super(StockFlowForm, self).__init__(*args, **kwargs)
self.fields['type'].widget.attrs['class'] = 'Entrada' if self.instance.type in [1,2,3] else 'Saida'
Now when you render this ModelForm in a template, the type field will have a dynamic class set to either 'Entrada' or 'Saida' depending upon the value of type.

Related

Directly show the menu when its action is added to a QToolBar

I have a menu that I want to add to a QToolBar.
I know that I can add the menuAction() of the menu to the tool bar, but while that will properly show the "menu hint" on its side and popup the menu by clicking on it, clicking the main area of the button will have no effect.
That action is not supposed to have any result when triggered: the menu is used to set the font color in a text editor, and since it automatically updates its icon based on the current color, making it checkable (to set/unset the font color) is ineffective.
What I want is that the menu will be shown, no matter where the user clicks.
I know that I can add the action, then use widgetForAction() to get the actual QToolButton, and then change its popupMode, but since I know that I will have more situations like this, I was looking for a better approach.
This answer suggests to use QPushButton instead, and add that button to the toolbar, but that solution is not ideal: QPushButton is styled slightly differently from the default QToolButton, and, as the documentation suggests, even if I use a QToolButton it will not respect the ToolButtonStyle.
Here is a basic MRE of my current code. Please consider that the ColorMenu class is intended to be extended for other features (background text, colors for table borders and backgrounds, etc) by using subclasses:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ColorMenu(QMenu):
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle('Text color')
self.group = QActionGroup(self)
iconSize = self.style().pixelMetric(QStyle.PM_LargeIconSize)
pm = QPixmap(iconSize, iconSize)
pm.fill(self.palette().text().color())
self.defaultAction = self.addAction(QIcon(pm), 'Default color')
self.defaultAction.setCheckable(True)
self.group.addAction(self.defaultAction)
self.addSeparator()
self.customColorAction = self.addAction('Custom color')
self.customColorAction.setVisible(False)
self.customColorAction.setCheckable(True)
self.group.addAction(self.customColorAction)
self.addSeparator()
self.baseColorActions = []
colors = {}
# get valid global colors
for key, value in Qt.__dict__.items():
if (
isinstance(value, Qt.GlobalColor)
and 1 < value < 19
):
# make names more readable
if key.startswith('light'):
key = 'light {}'.format(key[5:].lower())
elif key.startswith('dark'):
key = 'dark {}'.format(key[4:].lower())
colors[value] = key.capitalize()
# more logical sorting of global colors
for i in (2, 4, 5, 6, 3, 7, 13, 8, 14, 9, 15, 10, 16, 11, 17, 12, 18):
color = QColor(Qt.GlobalColor(i))
pm = QPixmap(iconSize, iconSize)
pm.fill(color)
action = self.addAction(QIcon(pm), colors[i])
action.setData(color)
action.setCheckable(True)
self.group.addAction(action)
self.baseColorActions.append(action)
self.setColor(None)
def setColor(self, color):
if isinstance(color, QBrush) and color.style():
color = color.color()
elif isinstance(color, (Qt.GlobalColor, int):
color = QColor(color)
if instance(color, QColor) and color.isValid():
for action in self.baseColorActions:
if action.data() == color:
self.setIcon(action.icon())
action.setChecked(True)
self.customColorAction.setVisible(False)
break
else:
iconSize = self.style().pixelMetric(QStyle.PM_LargeIconSize)
pm = QPixmap(iconSize, iconSize)
pm.fill(color)
icon = QIcon(pm)
self.setIcon(icon)
self.customColorAction.setIcon(icon)
self.customColorAction.setData(color)
self.customColorAction.setVisible(True)
self.customColorAction.setChecked(True)
return
self.setIcon(self.defaultAction.icon())
self.defaultAction.setChecked(True)
self.customColorAction.setVisible(False)
class Editor(QMainWindow):
def __init__(self):
super().__init__()
self.editor = QTextEdit()
self.setCentralWidget(self.editor)
self.formatMenu = self.menuBar().addMenu('Format')
self.colorMenu = ColorMenu(self)
self.formatMenu.addMenu(self.colorMenu)
self.toolbar = QToolBar('Format')
self.addToolBar(Qt.TopToolBarArea, self.toolbar)
self.toolbar.addAction(self.colorMenu.menuAction())
self.editor.currentCharFormatChanged.connect(self.updateColorMenu)
self.colorMenu.triggered.connect(self.setTextColor)
def setTextColor(self, action):
# assume that the action.data() has a color value, if not, revert to the default
if action.data():
self.editor.setTextColor(action.data())
else:
tc = self.editor.textCursor()
fmt = tc.charFormat()
fmt.clearForeground()
tc.setCharFormat(fmt)
def updateColorMenu(self, fmt):
self.colorMenu.setColor(fmt.foreground())
app = QApplication([])
editor = Editor()
editor.show()
app.exec()
A possibility is to use a subclass of QMenu and implement a specialized function that will return a dedicated action.
This is a bit of a hack/workaround, but may be effective in some situations.
That new action will:
be created by providing the tool bar, which will become its parent (to ensure proper deletion if the tool bar is destroyed);
forcibly show the menu when triggered;
update itself (title and icon) whenever the menu action is changed;
class ColorMenu(QMenu):
# ...
def toolBarAction(self, toolbar):
def triggerMenu():
try:
button = toolbar.widgetForAction(action)
if isinstance(button, QToolButton):
button.showMenu()
else:
print('Warning: action triggered from somewhere else')
except (TypeError, RuntimeError):
# the action has been destroyed
pass
def updateAction():
try:
action.setIcon(self.icon())
action.setText(self.title())
except (TypeError, RuntimeError):
# the action has been destroyed
pass
action = QAction(self.icon(), self.title(), toolbar)
action.triggered.connect(triggerMenu)
self.menuAction().changed.connect(updateAction)
action.setMenu(self)
return action
class Editor(QMainWindow):
def __init__(self):
# ...
# replace the related line with the following
self.toolbar.addAction(self.colorMenu.toolBarAction(self.toolbar))

How Can I add Choices in the Django User model

I want to add two choices to the Django User model and also I want to inherit a form from UserCreationForm that are derived from django.contrib.auth.forms.When I used the below code I got different error.Can anyone suggest a solution for this?
forms.py
TYPE_CHOICES =(
("individual", "Individual"),
("company", "Company"),
)
class UserForm(UserCreationForm):
first_name = forms.CharField(max_length=50)
type = forms.ChoiceField(choices=TYPE_CHOICES)
class Meta:
model = User
fields = ['first_name','type']
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
TYPE_CHOICES =(
("individual", "Individual"),
("company", "Company"),
)
class User(AbstractUser):
type = models.CharField(choices=TYPE_CHOICES,max_length=20)
You can use django model's choices for this. First create object for your choices.
your_choices = (
(1, u'Active'),
(2, u'Inactive')
)
Add these choices to your model class.
choice_field = models.IntegerField(choices=your_choices)
Now extend your form class.
class RegistrationForm(UserCreationForm):
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
email = forms.EmailField(max_length=75)
choice_field = forms.ChoiceField(choices=your_choices)
class Meta:
model = User
fields = ("first_name", "last_name", "email", "choice_field",)

How to define Auto evaluation and attempted multiple times constraint in MCQ for django model?

I am working on a MCQ based django project and based on my research I have below models that I got from 3rd party reference. Of course I had to make some modification based on my custom need. below is the snippet from models.py:
from django.db import models
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
class Quiz(models.Model):
name = models.CharField(max_length=1000)
questions_count = models.IntegerField(default=0)
description = models.CharField(max_length=70)
created = models.DateTimeField(auto_now_add=True,null=True,blank=True)
slug = models.SlugField()
roll_out = models.BooleanField(default=False)
class Meta:
ordering = ['created', ]
verbose_name_plural = 'Quizzes'
def __str__(self):
return self.name
class Question(models.Model):
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
label = models.CharField(max_length=1000)
order = models.IntegerField(default=0)
marks = models.IntegerField(default=0)
optional = models.BooleanField(default=False)
def __str__(self):
return self.label
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.CharField(max_length=1000)
is_correct = models.BooleanField(default=False)
def __str__(self):
return self.text
class QuizTakers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
correct_answers = models.IntegerField(default=0)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.username
class Response(models.Model):
quiztaker = models.ForeignKey(QuizTakers, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
answer = models.ForeignKey(Answer,on_delete=models.CASCADE,null=True,blank=True)
def __str__(self):
return self.question.label
#receiver(post_save, sender=Quiz)
def set_default_quiz(sender, instance, created, **kwargs):
quiz = Quiz.objects.filter(id=instance.id)
quiz.update(questions_count=instance.question_set.filter(quiz=instance.pk).count())
#receiver(post_save, sender=Question)
def set_default(sender, instance, created, **kwargs):
quiz = Quiz.objects.filter(id=instance.quiz.id)
quiz.update(questions_count=instance.quiz.question_set.filter(quiz=instance.quiz.pk).count())
#receiver(pre_save, sender=Quiz)
def slugify_title(sender, instance, *args, **kwargs):
instance.slug = slugify(instance.name)
However, there are couple of points that I have implement for my MCQs for which I am not able to find proper solution. Below are the points.
1) Every quiz can be attempted multiple times by the quiz taker. No restriction.
2) Since the questions are in MCQ format, the quiz taker answers are auto-evaluated by the application.
Any help would be highly appreciated.
Thank you.
To answer both your questions:
Since every quiz can be done multiple times you will simply have a reset button. Once that button is clicked, it will delete the QuizTaker. You will then be able to create it again.
When you create a quiz all the questions have answers and one of those answers is correct. Therefore, you can define a function that checks if the response provided is equal to the correct answer then increment the number of correct answers by 1 for each correct answer.
I hope this helps!!

Python 3.7: Detect name of property being accesed from class

So I am making a Class Which can dynamically return a property depending on whether or not a property was accessed on it. It also detects the name of the property when accessed by a class. my class code is as follows
class ConfigItem(object):
value: object
key:str
default: object
__name__:str = None
def __init__(self, default=None):
self.default: type(default) = default
self.value = default
self.key = "default_value"
def __get__(self, instance, owner):
if self.key:
self.value = os.environ.get(self.key,self.default)
else:
self.value = self.default
def __set_name__(self, owner, name):
self.__name__ = name
self.key = name
I want the code to have the following behavior:
when created like this:
a_test_key = ConfigItem('default_value')
a_test_key.key == 'a_test_key' #True
without having to pass the key into the constructor
and when accessed as so:
key_value = a_test_key
returns a_test_key.value
but when accessed any other way such as:
a_test_key.default
a_test_key.key
returns the respected values.
I think the solution has to do with the get(self, instance, owner) method, but I am unsure how to detect if a property has been accessed from ConfigItem.
Any takers on how to solve this problem?

How to disable scientific notation in QCompleter?

I have a table with some records that are keyed using a very large number as the primary key. I have code similar to the following which uses QCompleter to autocomplete lookups in this table. It works, but the displayed completions are formatted using the scientific notation (1234567 => 1.23e6). I'd like to have my completions displayed as-is. In my opinion, I'd either need to cast the response from the query as a string (can't figure out how to do this) or set a property on the QLineEdit to disable scientific notation formatting (can't figure this out either). Any ideas?
class MyDialog(BaseObject, QtGui.QDialog):
def __init__(self, ... db=None):
super(MyDialog, self).__init__(parent, logger)
self.qsql_db = db
self.init_ui()
def mk_model(self, completer, pFilterModel, table_name, filter_=None):
model = QtSql.QSqlTableModel()
model.setTable(table_name)
if filter_:
model.setFilter(filter_)
model.select()
pFilterModel.setSourceModel(model)
completer.setModel(pFilterModel)
return model
def setModelColumn(self, completer, pFilterModel, column):
completer.setCompletionColumn(column)
pFilterModel.setFilterKeyColumn(column)
def mk_receipt_id_grid(self):
font = self.mk_font()
label_receipt_id = QtGui.QLabel(self)
label_receipt_id.setText("Order ID")
label_receipt_id.setFont(font)
self.text_edit_receipt_id = QtGui.QLineEdit()
self.text_edit_receipt_id.setFont(font)
label_receipt_id.setBuddy(self.text_edit_receipt_id)
self.formGridLayout.addWidget(label_receipt_id, 0, 0)
self.formGridLayout.addWidget(self.text_edit_receipt_id, 0, 1)
self.connect(self.text_edit_receipt_id,
QtCore.SIGNAL("editingFinished()"),
self.get_order_details)
completer = QtGui.QCompleter(self)
completer.setCompletionMode(QtGui.QCompleter.UnfilteredPopupCompletion)
pFilterModel = QtGui.QSortFilterProxyModel(self)
pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
completer.setPopup(completer.popup())
self.text_edit_receipt_id.setCompleter(completer)
model = self.mk_model(completer, pFilterModel, "orders", "created_at > date_trunc('day', now())")
self.setModelColumn(completer, pFilterModel, model.fieldIndex("receipt_id"))
self.text_edit_receipt_id.textEdited.connect(pFilterModel.setFilterFixedString)
Screenshot of the issue:
One way to do this would be to set an item-delegate on the completer's view. The QStyledItemDelegate class has a displayText method that can be overridden, which makes implementing this quite easy.
Here is a simple demo:
import sys
from PySide import QtGui, QtCore
class ItemDelegate(QtGui.QStyledItemDelegate):
def displayText(self, data, locale):
if isinstance(data, (int, float)):
data = '%d' % data
return super(ItemDelegate, self).displayText(data, locale)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
edit = QtGui.QLineEdit()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(edit)
completer = QtGui.QCompleter(self)
model = QtGui.QStandardItemModel(self)
for value in (
17596767040000.0, 47993723466378568.0,
1219073478568475.0, 43726487587345.0,
29928757235623.0, 2245634345639486934.0,
):
item = QtGui.QStandardItem()
item.setData(value, QtCore.Qt.EditRole)
model.appendRow([item])
completer.setModel(model)
completer.popup().setItemDelegate(ItemDelegate(self))
edit.setCompleter(completer)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 50)
window.show()
sys.exit(app.exec_())

Resources