- ID
- d604a732-3d17-43ab-b564-b9fcb7d65f8e
Customer Dislikes — Cuisine, Dish Type, Ingredients, Flavors
TL;DR
Customers can express dislikes across all four dimensions, but the
mechanism differs by dimension. *Only ingredient dislikes are a hard
filter.* Cuisine, dish type, and flavor dislikes are soft signals that
nudge recommendations rather than excluding products.
| Dimension | Hard or Soft | Storage |
|------------+----------------+------------------------------------------|
| Cuisine | Soft | =Tag= + =CustomerTag= (TagGroup CUISINE) |
| Dish type | Soft | =DishPreference= (signed =rating=) |
| Ingredient | *Hard* | =Periodicity= (=NEVER= excludes) |
| Flavor | Soft | =Tag= + =CustomerTag= (multiple groups) |
Cuisine
- *Models:* =Tag= + =CustomerTag=
- *Files:* =app/models/tag.py:117-120=
- *Tag group:* =TagGroup.ID.CUISINE= (id =15=)
- *Dislike field:* =CustomerTag.preference= (=SmallIntegerField=, negative
= dislike); =Tag.dislike_score= (=IntegerField=) used in recommendation
scoring.
- *Hard or soft:* *Soft.* Feeds pairing recommendation and product
filtering scoring; does not exclude.
- *API:*
- =GET/POST /customers/{customer_id}/tags/= → =CustomerTagList=
- =GET/PUT /customers/{customer_id}/tags/{tag_group_id}/= for
cuisine-specific operations
- Serializer: =CustomerTagListSerializer= in
=app/rest/customer_tag.py=
Dish Type
- *Model:* =DishPreference=
- *File:* =app/models/dish_preference.py:7-10=
- *Relationship:* =ForeignKey= to =DishType= (line 9)
- *Dislike field:* =rating= (=SmallIntegerField=, default =0=). Negative
value = dislike, positive = like.
- *Hard or soft:* *Soft.* Used for dish-type personalization in
survey/preference flows.
- *API:* No dedicated REST endpoint. Surfaced indirectly via the
preference/survey endpoints.
Ingredient — Hard Exclusion
- *Model:* =Periodicity=
- *File:* =app/models/periodicity.py:7-9=
- *Relationship:* =ForeignKey= to =Product= (which carries ingredients)
- *Field:* =periodicity= (=PositiveSmallIntegerField=) with choices from
=PeriodicityConsts=: =NEVER=, =SOMETIMES=, =OFTEN=.
- *Hard or soft:* *HARD EXCLUSION.* When =periodicity = NEVER=, products
are filtered out entirely via =customer.get_never_products()=, which
is consumed by pairing/product query paths.
- *API:*
- =GET/POST /customers/{customer_id}/periodicities/= →
=PeriodicityListCreateAPIView= in
=app/rest/customer_periodicity.py=
Flavor
- *Models:* =Tag= + =CustomerTag= (same machinery as Cuisine)
- *File:* =app/models/tag.py:117-130=
- *Tag groups:*
- =SPICE_LEVEL= (id =6=)
- =FLAVOR_PROFILE= (id =11=)
- =FLAVOUR= (id =22=) — e.g. tags =HOT= (22), =MILD= (20), =MEDIUM= (21)
- *Dislike fields:* =CustomerTag.preference= (negative);
=Tag.dislike_score=, =Tag.pairing_dislike_score=.
- *Hard or soft:* *Soft.* Scoring-based for recommendations.
- *API:* Same surface as Cuisine — =/customers/{customer_id}/tags/= and
=/customers/{customer_id}/tags/{tag_group_id}/=.
Related: Post-Purchase Feedback (not a preference)
- *Model:* =CustomerFoodFeedback=
- *File:* =app/models/customer_food_feedback.py:15-26=
- Captures feedback tags (taste, ingredients, nutrition) *after* the
customer consumes the item. The =preference= field can be negative
(dislike), but this is a feedback mechanism, not a pre-purchase
preference declaration. Worth distinguishing from the four
preference dimensions above when reasoning about whether a dislike
filters out a product before it's recommended.
Open Questions / Follow-ups
- For the soft dimensions (cuisine, dish type, flavor), what are the
current *scoring weights* and where do they live? Need to trace into
pairing recommendation scoring to know how strong a "dislike" signal
actually is in practice.
- Does =CustomerFoodFeedback= feed back into the soft dislike signals
on subsequent recommendations, or is it purely operational/CX data?
- For UX: only ingredient dislikes hard-block products. If a customer
expects "I disliked Italian" to *exclude* Italian dishes, that
expectation is not met today. Worth flagging.