I am trying to restrict the dropdown options according to the user type within the same form
forms.py
class EForm(forms.ModelForm):
class Meta:
model = Model
fields = ('t','s','q')
I think you need to override the entire field.
https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#overriding-the-default-fields
So set one of your options (the most common one) as the default on the form - add this code to your list of fields (i.e. above Meta)
status = forms.Select(
widget=forms.Select(choices=CHOICES_OPTIONS_1),
)
Then override the widget if you need to by passing a new dict - you can replace your current if .. else with this bit.
if not self.user.is_usertypeA:
widgets = {
'status': Select(choices=CHOICES_OPTIONS_2),
}
I've not tested this, but it should work.
Note the options 1 and 2 are meant to represent the tuples you give in the question - so the first one is C and the second one would be A´ and B` in your example.
Not entirely convinced __init__ is the place to do this either, but if it works ...
Related
Let's say I have these models:
class Material(models.Model):
name = models.CharField([...])
class Consumable(models.Model):
name = models.CharField([...])
restores = models.IntegerField([...])
class Weapon(models.Model):
name = models.CharField([...])
damage = models.IntegerField([...])
# And then I have an 'inventory', like this one:
class Inventory(models.Model):
user = models.ForeignKey([...]) # to which user you want to link the item
item = models.ForeignKey([...]]) # which item
quantity = models.IntegerField([...]) # how many of it
I want to be able to have all Material, Consumable, and Weapon models listed in the 'item' field, so when you want to add an item as an inline, you would see all 3 models' objects.
Something like
# instead of this
item = models.ForeignKey(Consumable) # which item
# want something like this
item = models.ForeignKey(Consumable and Material and Weapon) # which item
# this wouldn't work ofc...
Is there a way to collect all 3 of them and pass them to the 'item' field, without the need of restarting the server? (when making a "choices" list that queries from a model you must restart the server to see the newly added objects, I don't want that.)
I also want to stick to the built-in admin of Django since it provided everything I need for the past couple of months, but I am open to any ideas.
I could be wrong but I think you are making this more complex than it needs to be. Instead of doing separate classes for materials (type of material) and consumable (type of product), you can have that built in the last class as model field as category or bolean fields.
class Products(models.Model):
material_type =
consumable = boolean for yes no or you can do multiple choice field
Then for items you can query the number of items based on material_type or consumable model fields (see query filters for for more).
all_items = Products.model.all()
consumable_items = Products.model.filter(your filter logic goes here)
Hope this helps!
What I'm trying to do is get a list of all the fields in a serializer which:
Do not contain required=False as a parameter.
Do not come from the parent serializer.
For example, if I have serializers like:
class ParentSerializer(serializers.Serializer):
parent_field = serializers.IntegerField
class ChildSerializer(ParentSerializer):
child_field_required = serializers.IntegerField
child_field_not_required = serializers.IntegerField(required=False)
I'd like the resulting output to be:
['child_field_required']
I've figured out I can get an ordered list of the declared fields like:
self.get_serializer().get_fields()
>> OrderedDict([
('parent_field', IntegerField()),
('child_field_required', IntegerField()),
('child_field_not_required', IntegerField(required=False)),
])
But I haven't been able to move past this step.
As you're inhering from serializers.Serializer, all fields must be declared explicitly on the class body (unlike serializers.ModelSerializer which traverses to the model layer to generate fields automatically). In that case, you can do:
parent_field_names = {
name
for base in type(instance).__bases__
if hasattr(base, '_declared_fields')
for name in base._declared_fields
}
desired_fields = [
(name, field)
for name, field in instance.get_fields().items()
if name not in parent_field_names and
field._kwargs.get('required', None) is not False
]
Assuming instance is a serializers.Serializer instance. parent_field_names refers to a set for O(1) lookups.
The above depends on a couple of implementation details:
The metaclass of serializers.Serializer -- serializers.SerializerMetaclass assigns the declared fields as class attribute _declared_fields dict
Whole constructing (__new__), the serializers.Field class preserves the initially passed keyword arguments as the _kwargs attribute on the instance to the newly created field instance
Notes:
If you want to make this applicable to serializers.ModelSerializer's model fields traversal as well i.e. for non-declared fields or fields overridden in Meta, you need to check for get_fields of each and then get only the fields that are defined in the current serializer
serializers.SerializerMetaclass pops off the fields from class attributes while creating the _declared_fields attribute, so you can't access the fields from class. Otherwise, we could use the attribute check on the base classes to see if any field is defined there, which would result in a shorter code.
I've come up with a solution, but I feel like there is a more elegant one out there...
instance = ChildSerializer()
serializer_fields = instance.get_fields()
parent_serializer_fields = instance.__class__.__bases__[0]().get_fields()
child_fields = {k: serializer_fields[k] for k in set(serializer_fields) - set(parent_serializer_fields)}
required_fields = [k for k, v in child_fields.items() if v.required]
How do you get the other side of a symmetrical self reference in a M2M django model?
Say we are making a parts catalog, and we have lots of Parts and we want to have an Interchange to show which parts can be used in place of another:
class Part(models.Model):
name = models.CharField(max_length=300)
number = models.CharField(max_length=200, default 'because numberphiles like more ids')
…
interchanges = models.ManyToManyField("self",
through='Interchange',
symmetrical=False,
# + sign per http://charlesleifer.com/blog/self-referencing-many-many-through/
# I have tried without the plus t omaintain the references as well, but can't seem to understand the difference(s)
related_name="interchanges_to+",
# through_fields per docs
through_fields=('reference', 'interchange')
)
# per this http://charlesleifer.com/blog/self-referencing-many-many-through/
def add_interchange(self, part, symm=True):
interchange = models.Interchange.objects.get_or_create(
reference_part=self,
interchange_part=part)
if symm:
# avoid recursion by passing `symm=False`
part.add_interchange(self, False)
return interchange
def remove_interchange(self, part, symm=True):
models.Interchange.objects.filter(
reference_part=self,
interchange_part=part).delete()
if symm:
# avoid recursion by passing `symm=False`
part.remove_interchange(self, False)
def get_interchanges(self):
return ", ".join([str(p) for p in self.interchanges.filter(interchange_part__reference=self)])
def get_references(self):
return …?
# This is where I don't know how to get the Parts that are referring to it as being an interchange
class Interchange(models.Model):
reference = models.ForeignKey(Part, related_name="reference_part")
interchange = models.ForeignKey(Part, related_name="interchange_part")
# just to confirm we have a M2M table, we will add a foregin key to a User if this interchange has been personally verified
confirmed_by = models.ForeignKey(User, null=True, blank=True)
# … others if needed
class Meta:
# because self-referencing, in this case, is silly, we make sure each part references another part
unique_together = ('reference', 'interchange')
in the Django admin, I have:
#admin.register(app.Part)
class PartAdmin(admin.ModelAdmin):
list_display = ['name',
'number',
# ▼ I can see the Parts itself Interchanges to
'get_interchanges',
# ▼ I can not see the Parts that reference itself as being an Interchange
'get_references']
Parts Model in admin for reference...
Here is what I have:
Here is what I am trying to get:
As a confirmation, the Interchange Model in admin:
How do you get the other side of symmetrical self-reference M2M django models?
How do I return Parts that are in the interchange table but in the reference (first) column
Notes:
using django 1.11
I know that the symmetry being False is required when using a M2M through Model per the docs
This nice write-up helped me get quite far, but in the admin, I can't seem to get the interchange_part to show its reference_parts in the admin page (thusly I haven't even tried to see if it is possible in a view with different/particular function)
In the docs it states the second part of the tuple for through_keys is called the target model, but I cant find out what to refer to the first one
I am sure that there is a way to go multiple levels deep, ie if part one interchanges to 2, and 2 => 3, then 1 should list 2 and 3 (but that isn't the focus at the moment)
Please correct my misunderstandings, Thanks :)
If the manytomany has a through table with a related_name parameter and does NOT include the +, then you can just access the set directly :
def get_references(self):
return ", ".join([str(p) for p in self.interchanges_to.all()])
Note, I am unsure about performance/complexity
I am trying to retrieve comment field(customer internal notes) from res_partner to account invoice module.Right now I just want to print it later I will include it in xml code. I tried in three ways like this,
1)comment2 = fields.Char(string='Comment',related='res_partner.comment',compute='_compute_com')
#api.multi
def _compute_com(self):
print self.comment2
2)comment = fields.Many2one('res.partner','Comment',compute='_compute_com')
#api.multi
def _compute_com(self):
print self.comment
3)partner_comment = fields.Char(compute='_compute_com')
#api.multi
def _compute_com(self):
Comment = self.env['res.partner'].browse(partner_id).comment
print Comment
You should use a related field instead:
comment = fields.Char(related='partner_id.comment')
If you need to store it in your account_invoice record you also need to add the parameter store=True
Problem is, this way you can't just print it but if you need to show it you need to put it into your view.
If you really need to print it temporarly you need to do this other way:
comment = fields.Char(compute='_compute_comment')
def _compute_comment(self):
for record in self:
record.comment = partner_id.comment
print record.comment
Related Field
There is not anymore fields.related fields.
Instead you just set the name argument related to your model:
participant_nick = fields.Char(string='Nick name',
related='partner_id.name')
The type kwarg is not needed anymore.
Setting the store kwarg will automatically store the value in database. With new API the value of the related field will be automatically updated, sweet.
participant_nick = fields.Char(string='Nick name',
store=True,
related='partner_id.name')
Note
When updating any related field not all translations of related field
are translated if field is stored!!
Chained related fields modification will trigger invalidation of the cache for all elements of the chain.
in odoo8
if need same object fields to related then you can use related="related field name " use store=True
comment2 = fields.Char(string='comment',related='comment', store=True)
LINK
Be careful, you have to use the same kind of field !!
(I had the problem with Selection and Char ... so I have to use Selection with Selection)
[edit]
Since I have removed the new_method = ... and syspar.method.add() lines (this is taken care of by method.save()), the solution that I originally discarded has become DRY, so it is perfectly acceptable.
I do still think it strange that setting self.fields[].initial (as per pyriku's answer) only works if extra > 0, because the simple use case where you don't want a form displayed until an 'add form' button is clicked, must have extra=0 and it should be possible to set initial values for the newly added form.
[/edit]
Disclaimer:
I have looked at many answers to related questions, and I have only found one solution that works, but appears crufty to me and not very DRY (4378667) /Disclaimer
I have a modelformset where I set extra to 0, because I only want to display a new form when the user presses "add_method". Everything works fine, except that the newly added forms are empty (desired behaviour: they contain the default value for name that I set in the Method table)
In models.py:
class System(models.Model):
method = models.ManyToManyField(Method)
...
class Method(models.Model):
name = models.CharField()
...
In view.py:
syspar = System.objects.get(id=sys_id)
MethodFormSet = modelformset_factory(Method, form=MethodForm, extra=0)
if request.POST:
if 'add_method' in request.POST:
new_method = Method.objects.create(name='whatever')
syspar.method.add(new_method)
post = request.POST.copy()
post['method-TOTAL_FORMS'] = repr(int(post['method-TOTAL_FORMS'])+ 1)
post['method-2-name'] = 'whatever' # code needed here to get actual value of -2-
methodformset = MethodFormSet(data=post, prefix='method',
queryset=Method.objects.filter(id__in=syspar.method.all()))
I have a problem with the line: post['method-2-name']=''
This is awful, because:
I have already set the value in the db table
I will need extra cruft to determine the value of '2' for each case
I tried this approach, also tried setting initial (as suggested in answers to some other related questions), but neither worked and both also resulted in non-DRY code.
There must be a better way to do this...
Probably you will need to override the __init__() method of that form for setting the initial values that you want. Something like:
def __init__(self, *args, **kwargs):
super(MethodForm, self).__init__(*args, **kwargs)
self.initial['field'] = 'value'